blob: abf0659c8dade5697a0475cc47f34b7b0a4b645d [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2018 Mettenmeier GmbH
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
package org.eclipse.openk.sp.controller.planning;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.eclipse.openk.sp.controller.AbstractController;
import org.eclipse.openk.sp.controller.validation.ValidationController;
import org.eclipse.openk.sp.db.dao.CalendarRepository;
import org.eclipse.openk.sp.db.dao.StandbyDurationRepository;
import org.eclipse.openk.sp.db.dao.StandbyGroupRepository;
import org.eclipse.openk.sp.db.dao.StandbyListRepository;
import org.eclipse.openk.sp.db.dao.StandbyScheduleBodyRepository;
import org.eclipse.openk.sp.db.dao.StandbyStatusRepository;
import org.eclipse.openk.sp.db.dao.UserInStandbyGroupRepository;
import org.eclipse.openk.sp.db.dao.UserRepository;
import org.eclipse.openk.sp.db.model.CalendarDay;
import org.eclipse.openk.sp.db.model.StandbyDuration;
import org.eclipse.openk.sp.db.model.StandbyGroup;
import org.eclipse.openk.sp.db.model.StandbyScheduleBody;
import org.eclipse.openk.sp.db.model.User;
import org.eclipse.openk.sp.db.model.UserInStandbyGroup;
import org.eclipse.openk.sp.dto.StandbyScheduleBlueprintDto;
import org.eclipse.openk.sp.dto.planning.PlanningBodyResultDto;
import org.eclipse.openk.sp.dto.planning.PlanningMsgDto;
import org.eclipse.openk.sp.dto.planning.PlanningMsgResponseDto;
import org.eclipse.openk.sp.dto.planning.PlanningPhaseDto;
import org.eclipse.openk.sp.dto.planning.StandbyScheduleActionDto;
import org.eclipse.openk.sp.exceptions.SpErrorEntry;
import org.eclipse.openk.sp.exceptions.SpException;
import org.eclipse.openk.sp.exceptions.SpExceptionEnum;
import org.eclipse.openk.sp.mail.MailRequest;
import org.eclipse.openk.sp.util.DateHelper;
import org.eclipse.openk.sp.util.SpMsg;
import org.eclipse.openk.sp.util.ValidationHelper;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
/** Class to handle planning operations. */
public class PlanningController extends AbstractController {
protected static final Logger LOGGER = Logger.getLogger(PlanningController.class);
public static final String TXT_AUTOMATIC_PLANNING = "autom. Planung";
public static final String TXT_AUTOMATIC_PLANNING_HOLIDAY = "autom. Planung (Dienstfrei)";
public static final String TXT_AUTOMATIC_CHANGE = "autom. Ă„nderung";
public static final String TXT_MANUAL_CHANGE = "manuelle Ă„nderung";
public static final String TXT_AND_MORE = "und weitere";
@Autowired
private StandbyGroupRepository standbyGroupRepository;
@Autowired
private StandbyListRepository standbyListRepository;
@Autowired
private StandbyDurationRepository standbyDurationRepository;
@Autowired
private StandbyStatusRepository standbyStatusRepository;
@Autowired
private ArchiveController archiveController;
@Autowired
private StandbyScheduleBodyRepository standbyScheduleBodyRepository;
@Autowired
private UserInStandbyGroupRepository uisgRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private CalendarRepository calendarRepository;
@Autowired
private ValidationController validatonController;
@Autowired
private MailRequest mailing;
private List<PlanningMsgDto> lsPlanningMsg = new ArrayList<>();
private int warningCounter = 0;
public PlanningMsgResponseDto startPlanning(StandbyScheduleBlueprintDto standbyBlueprintDto, String username)
throws SpException {
PlanningMsgResponseDto responseDto = new PlanningMsgResponseDto();
try {
lsPlanningMsg = new ArrayList<>();
warningCounter = 0;
// check if input is valid
if (this.validateInputForPlanCalculation(standbyBlueprintDto)) {
standbyBlueprintDto.setValidTo(DateHelper.getEndOfDay(standbyBlueprintDto.getValidTo()));
// calculate the different phases.
Map<Integer, PlanningPhaseDto> planningPhaseMap = this.calculatePlanningPhaseMap(standbyBlueprintDto);
this.createMsgNumberOfPhases(planningPhaseMap, standbyBlueprintDto);
Long lastStartUserId = standbyBlueprintDto.getStartIdOfUser();
// loop over planning phases
// delete existing entries
StandbyGroup stbyGroup = standbyGroupRepository.findOne(standbyBlueprintDto.getStandbyGroupId());
Date lastCalcDate = DateHelper
.getEndOfDay(new DateTime(standbyBlueprintDto.getValidTo()).minusSeconds(1).toDate());
this.changeOrDeleteExistingBodies(stbyGroup,
new DateTime(DateHelper.getStartOfDay(standbyBlueprintDto.getValidFrom())).plusSeconds(1)
.toDate(),
lastCalcDate, 1L, username);
for (Entry<Integer, PlanningPhaseDto> entry : planningPhaseMap.entrySet()) {
LOGGER.debug("Start Plannig for phase " + entry.getKey());
PlanningPhaseDto dto = entry.getValue();
this.createMsgStartOfPhase(entry.getKey(), dto);
PlanningBodyResultDto planningBodyResultDto = this.calculatePlanForPhase(dto, lastStartUserId,
username, entry.getKey(), lastCalcDate);
lastStartUserId = planningBodyResultDto.getLastUserId();
this.createMsgEndOfPhase(entry.getKey());
LOGGER.debug("End Plannig for phase " + entry.getKey());
}
standbyScheduleBodyRepository.flush();
// start validation
List<String> lsValidationBeanNames = new ArrayList<>();
lsValidationBeanNames.add(ValidationController.BEAN_GROUP_COVERAGE_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_DOUBLE_PLANNED_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_PLACEHOLDER_STANDBY_USER_VALIDATOR);
List<StandbyGroup> lsStandbyGroups = new ArrayList<>();
lsStandbyGroups.add(standbyGroupRepository.findOne(standbyBlueprintDto.getStandbyGroupId()));
validatonController.startValidation(standbyBlueprintDto.getValidFrom(),
standbyBlueprintDto.getValidTo(), lsStandbyGroups, lsValidationBeanNames,
standbyBlueprintDto.getStatusId(), null);
responseDto.setLsMsg(lsPlanningMsg);
}
return responseDto;
} catch (SpException e) {
LOGGER.error(e, e);
throw e;
} catch (Exception e) {
LOGGER.error(e, e);
SpErrorEntry ee = SpExceptionEnum.DEFAULT_EXCEPTION.getEntry();
ee.setE(e);
throw new SpException(ee);
}
}
/**
* Method to calculate the different phases for changing groups.
*
* @param standbyBlueprintDto
* @return
* @throws SpException
*/
public Map<Integer, PlanningPhaseDto> calculatePlanningPhaseMap(StandbyScheduleBlueprintDto standbyBlueprintDto)
throws SpException {
Map<Integer, PlanningPhaseDto> resultMap = new HashMap<>();
try {
// start of full planning time slot.
Date validFrom = standbyBlueprintDto.getValidFrom();
// end of full planning time slot.
DateTime dt = new DateTime(standbyBlueprintDto.getValidTo());
dt = dt.minusSeconds(1);
Date validTo = dt.toDate();
// temporal date for calculation
Date tmpDate = validFrom;
StandbyGroup group = standbyGroupRepository.findOne(standbyBlueprintDto.getStandbyGroupId());
StandbyDuration firstDuration = this.getFittingDurationOfGroup(group, validFrom);
if (firstDuration != null) {
tmpDate = DateHelper.getDateWithTime(tmpDate, firstDuration.getValidFrom());
tmpDate = new DateTime(tmpDate).plusSeconds(1).toDate();
}
int phase = 1;
int differenceOfDays = 0;
while (tmpDate.getTime() < validTo.getTime()) {
// get fitting duration
List<StandbyDuration> lsDur = this.getFittingDurationsAroundDate(group, tmpDate);
StandbyDuration duration = null;
if (lsDur != null && !lsDur.isEmpty()) {
duration = lsDur.get(lsDur.size() - 1);
// current week day
int tempDateDay = DateHelper.getDayOfWeek(tmpDate);
// get days between duration start and duration end
int differenceOfDurationDays = DateHelper.calculateDifferenceOfDays(duration.getValidDayFrom(),
duration.getValidDayTo(), duration.getValidFrom(), duration.getValidTo());
// get days between duration start and the current date (tmpDate)
int differenceOfTempDateAndDurStart = DateHelper.calculateDifferenceOfDays(
duration.getValidDayFrom(), tempDateDay, duration.getValidFrom(), tmpDate);
// reduce duration slot with the starting difference
differenceOfDays = differenceOfDurationDays - differenceOfTempDateAndDurStart;
Date startOfDuration = DateHelper.getDateWithTime(tmpDate, duration.getValidFrom());
Date endOfDuration = DateHelper.addDaysToDate(tmpDate, differenceOfDays);
endOfDuration = DateHelper.getDateWithTime(endOfDuration, duration.getValidTo());
if (!duration.getNextUserInNextDuration()) {
// find next duration with a changing user allowed
StandbyDuration tmpDur = this.getNextDurationWithMaChange(group, endOfDuration);
if (tmpDur != null) {
differenceOfDays = DateHelper.calculateDifferenceOfDays(
DateHelper.getDayOfWeek(startOfDuration), tmpDur.getValidDayTo(), startOfDuration,
tmpDur.getValidTo());
endOfDuration = DateHelper.addDaysToDate(tmpDate, differenceOfDays);
endOfDuration = DateHelper.getDateWithTime(endOfDuration, tmpDur.getValidTo());
} else {
LOGGER.debug("Keine Duration fĂ¼r den Tag : " + startOfDuration + " gefunden.");
}
}
if (DateHelper.isDateAfter(endOfDuration, validTo)) {
endOfDuration = validTo;
}
List<UserInStandbyGroup> lsUserInGroup = uisgRepository
.findUserForInterval(standbyBlueprintDto.getStandbyGroupId(), tmpDate, endOfDuration);
if (lsUserInGroup != null && !lsUserInGroup.isEmpty()) {
if (resultMap.containsKey(phase)) {
PlanningPhaseDto dto = resultMap.get(phase);
if (CollectionUtils.isEqualCollection(dto.getLsUsers(), lsUserInGroup)) {
dto.setEndDate(endOfDuration);
} else {
phase++;
LOGGER.debug("Add phase " + phase);
resultMap.put(phase,
new PlanningPhaseDto(dto.getEndDate(), endOfDuration, lsUserInGroup));
}
} else {
LOGGER.debug("Add phase " + phase);
resultMap.put(phase, new PlanningPhaseDto(startOfDuration, endOfDuration, lsUserInGroup));
}
} else {
LOGGER.debug("Keine Gruppe gefunden!");
}
if (differenceOfDays == 0) {
// add at least one day
differenceOfDays = 1;
}
tmpDate = DateHelper.addDaysToDate(tmpDate, differenceOfDays);
}
}
this.logPhases(resultMap);
return resultMap;
} catch (Exception e) {
LOGGER.error(e, e);
SpErrorEntry ee = SpExceptionEnum.DEFAULT_EXCEPTION.getEntry();
ee.setE(e);
throw new SpException(ee);
}
}
/**
* Method to search the next standby duration for a group that has the
* getNextUserInNextDuration flag = true.
*
* @param group
* @param date
* @return
*/
public StandbyDuration getNextDurationWithMaChange(StandbyGroup group, Date date) {
for (int i = 0; i <= 6; i++) {
List<StandbyDuration> lsDuration = this.getFittingDurationsByValidTo(group, date);
for (StandbyDuration stbDur : lsDuration) {
if (stbDur.getNextUserInNextDuration()) {
return stbDur;
}
}
date = DateHelper.addDaysToDate(date, 1);
}
return null;
}
public void changeOrDeleteAtIntervalStart(StandbyScheduleBody body, StandbyDuration duration, Date tmpDate,
String username) {
if (duration != null) {
Date durationStart = DateHelper.getDateWithTime(tmpDate, duration.getValidFrom());
if (DateHelper.isDateBefore(body.getValidFrom(), durationStart)) {
if (DateHelper.isDateAfter(body.getValidTo(), durationStart)) {
body.setValidTo(durationStart);
body.setModificationDate(new Date());
body.setModifiedCause(TXT_AUTOMATIC_CHANGE);
body.setModifiedCause(username);
standbyScheduleBodyRepository.save(body);
}
} else {
standbyScheduleBodyRepository.delete(body);
}
}
}
public void changeOrDeleteAtIntervalEnd(StandbyScheduleBody body, StandbyDuration duration, Date tmpDate,
String username) {
if (duration != null) {
Date durationEnd = DateHelper.getDateWithTime(tmpDate, duration.getValidTo());
if (DateHelper.isDateBefore(body.getValidFrom(), durationEnd)) {
if (DateHelper.isDateAfter(body.getValidTo(), durationEnd)) {
body.setValidTo(durationEnd);
body.setModificationDate(new Date());
body.setModifiedCause(TXT_AUTOMATIC_CHANGE);
body.setModifiedCause(username);
standbyScheduleBodyRepository.save(body);
}
} else {
standbyScheduleBodyRepository.delete(body);
}
}
}
public void changeOrDeleteExistingBodies(StandbyGroup group, Date startDate, Date endDate, Long statusId,
String username) {
startDate = DateHelper.getStartOfDay(startDate);
Date tmpDate = startDate;
while (tmpDate.getTime() <= endDate.getTime()) {
List<StandbyScheduleBody> lsBodies = standbyScheduleBodyRepository
.findByGroupAndStatusHittingDateInterval(group.getId(), tmpDate, endDate, statusId);
List<StandbyDuration> lsFittingDurations = this.getFittingDurationsOfGroup(group, tmpDate);
StandbyDuration duration = null;
if (lsFittingDurations != null && !lsFittingDurations.isEmpty()) {
duration = lsFittingDurations.get(0);
}
for (StandbyScheduleBody body : lsBodies) {
// if (tmpDate.getTime() == endDate.getTime()) {
// this.changeOrDeleteAtIntervalEnd(body, duration, tmpDate, username);
// } else
if (body.getValidFrom().getTime() >= startDate.getTime()
&& body.getValidTo().getTime() <= endDate.getTime()) {
standbyScheduleBodyRepository.delete(body);
}
}
tmpDate = DateHelper.addDaysToDate(tmpDate, 1);
}
}
public PlanningBodyResultDto calculatePlanForPhase(PlanningPhaseDto dto, Long lastStartUserId, String username,
int phaseNumber, Date lastCalcDate) throws SpException {
try {
List<UserInStandbyGroup> lsUserInGroup = dto.getLsUsers();
Long lastTurnUserId = lastStartUserId;
Collections.sort(lsUserInGroup,
(UserInStandbyGroup o1, UserInStandbyGroup o2) -> o1.getPosition().compareTo(o2.getPosition()));
int currentPosition = this.getIndexOfLastPlannedUser(lastStartUserId, lsUserInGroup);
if (currentPosition == -1) {
this.createMsgGroupLeaderChanged(userRepository.findOne(lastStartUserId), lsUserInGroup);
currentPosition = 0;
}
Date tmpDate = dto.getStartDate();
PlanningBodyResultDto planningBodyResultDto = null;
while (tmpDate.getTime() < dto.getEndDate().getTime()) {
// reset position if end of list has been reached
planningBodyResultDto = this.calculateScheduleBodyEntries(lsUserInGroup, dto.getStartDate(), tmpDate,
lastCalcDate, lastTurnUserId, username, lastStartUserId, phaseNumber);
tmpDate = planningBodyResultDto.getTempDate();
currentPosition = planningBodyResultDto.getNewPosition();
lastStartUserId = planningBodyResultDto.getLastStartUserId();
lastTurnUserId = planningBodyResultDto.getLastUserId();
this.addMessagesToList(planningBodyResultDto.getLsMsg());
}
planningBodyResultDto.setLastUserId(lastTurnUserId);
return planningBodyResultDto;
} catch (Exception e) {
LOGGER.error(e, e);
SpErrorEntry ee = SpExceptionEnum.DEFAULT_EXCEPTION.getEntry();
ee.setE(e);
throw new SpException(ee);
}
}
/**
* Method to create a simple {@link StandbyScheduleBody} object with input
* values for planning.
*
* @param group
* @param username
* @param date
* @param user
* @param duration
* @return
*/
public StandbyScheduleBody createScheduleBodyObject(StandbyGroup group, String username, Date date, User user,
StandbyDuration duration, String cause) {
StandbyScheduleBody stbyBody = new StandbyScheduleBody();
stbyBody.setModificationDate(new Date());
stbyBody.setModifiedBy(username);
stbyBody.setModifiedCause(cause);
stbyBody.setStandbyGroup(group);
stbyBody.setUser(user);
stbyBody.setStatus(standbyStatusRepository.findOne(1l));
stbyBody.setValidFrom(DateHelper.getDateWithTime(date, duration.getValidFrom()));
stbyBody.setValidTo(DateHelper.getDateWithTime(date, duration.getValidTo()));
return stbyBody;
}
public List<StandbyScheduleBody> createOverlappingScheduleBodyObjects(StandbyGroup group, String username,
Date date, User user, StandbyDuration duration, int loopPos, int loopLength) {
List<StandbyScheduleBody> lsStandbyScheduleBodies = new ArrayList<>();
StandbyScheduleBody stbyBody = this.createScheduleBodyObject(group, username, date, user, duration,
TXT_AUTOMATIC_PLANNING);
if (loopPos > 0) {
// if not the first loop the schedule body should start at the begin of the day.
stbyBody.setValidFrom(DateHelper.getDateWithTime(date, DateHelper.getStartOfDay(duration.getValidFrom())));
}
Date validTo = DateHelper.getDateWithTime(date, duration.getValidTo());
validTo = DateHelper.getEndOfDay(validTo);
stbyBody.setValidTo(validTo);
stbyBody = this.calculateHoliday(group, date, stbyBody, false);
lsStandbyScheduleBodies.add(stbyBody);
if (loopPos + 1 == loopLength) {
// if last entry use end time of duration
StandbyScheduleBody stbyBody2 = null;
stbyBody2 = this.createScheduleBodyObject(group, username, date, user, duration, TXT_AUTOMATIC_PLANNING);
Date nextDay = DateHelper.addDaysToDate(date, 1);
Date validFrom = DateHelper.getDateWithTime(DateHelper.addDaysToDate(date, 1),
DateHelper.getStartOfDay(duration.getValidFrom()));
stbyBody2.setValidFrom(validFrom);
validTo = DateHelper.getDateWithTime(nextDay, duration.getValidTo());
stbyBody2.setValidTo(validTo);
this.calculateHoliday(group, nextDay, stbyBody2, false);
lsStandbyScheduleBodies.add(stbyBody2);
}
return lsStandbyScheduleBodies;
}
/**
* Method to check if the current day is a holiday for the standby group and
* calls the pre-draw or extend calculation.
*
* @param group
* @param date
* @param stbyBody
* @return
*/
public StandbyScheduleBody calculateHoliday(StandbyGroup group, Date date, StandbyScheduleBody stbyBody,
boolean isSingleDay) {
if (isHoliday(date, group)) {
if (group.getExtendStandbyTime()) {
// if extend of standby time is needed
stbyBody = this.calculateHolidayExtend(group, date, stbyBody);
if (isSingleDay) { // additional calculation if no day overlapping time slot
stbyBody = this.calculateHolidayPreDraw(group, date, stbyBody);
}
} else {
// is pre-draw of standby time needed
stbyBody = this.calculateHolidayPreDraw(group, date, stbyBody);
if (isSingleDay) { // additional calculation if no day overlapping time slot
stbyBody = this.calculateHolidayExtend(group, date, stbyBody);
}
}
}
return stbyBody;
}
/**
* Method to change the standby time if a holiday / calendar day is defined and
* the standby time has to be pre-drawn.
*
* @param group
* @param date
* @param stbyBody
* @return
*/
public StandbyScheduleBody calculateHolidayPreDraw(StandbyGroup group, Date date, StandbyScheduleBody stbyBody) {
List<StandbyDuration> lsDurations = this.getFittingDurationsByValidTo(group, date);
if (lsDurations != null) {
int size = lsDurations.size();
// loop backward to find last fitting duration
for (int a = size; a > 0; a--) {
StandbyDuration dur = lsDurations.get(a - 1);
// proof if found duration ends before current body starts.
if (dur != null && DateHelper.isDateBefore(DateHelper.getDateWithTime(date, dur.getValidTo()),
stbyBody.getValidFrom())) {
// set last found duration end as start of current entry
stbyBody.setValidFrom(DateHelper.getDateWithTime(date, dur.getValidTo()));
this.createMsgExtendHoliday(stbyBody, date, false);
// end loop after first fitting result.
stbyBody.setModifiedCause(TXT_AUTOMATIC_PLANNING_HOLIDAY);
return stbyBody;
}
}
// no last duration for the day has been found. Therefore the start of day has
// been used for current entry
if (DateHelper.isDateBefore(DateHelper.getStartOfDay(date), stbyBody.getValidFrom())) {
stbyBody.setValidFrom(DateHelper.getDateWithTime(date, DateHelper.getStartOfDay(date)));
this.createMsgExtendHoliday(stbyBody, date, false);
stbyBody.setModifiedCause(TXT_AUTOMATIC_PLANNING_HOLIDAY);
}
} else {
// no last duration for the day has been found. Therefore the start of day has
// been used for current entry
if (DateHelper.isDateBefore(DateHelper.getStartOfDay(date), stbyBody.getValidFrom())) {
stbyBody.setValidFrom(DateHelper.getDateWithTime(date, DateHelper.getStartOfDay(date)));
this.createMsgExtendHoliday(stbyBody, date, false);
stbyBody.setModifiedCause(TXT_AUTOMATIC_PLANNING_HOLIDAY);
}
}
return stbyBody;
}
/**
* Method to change the standby time if a holiday / calendar day is defined and
* the standby time has to be extended.
*
* @param group
* @param date
* @param stbyBody
* @return
*/
public StandbyScheduleBody calculateHolidayExtend(StandbyGroup group, Date date, StandbyScheduleBody stbyBody) {
List<StandbyDuration> lsDurations = this.getFittingDurationsOfGroup(group, date);
if (lsDurations != null) {
int size = lsDurations.size();
// loop forward to find fitting duration that starts after current schedule body
for (int a = 0; a < size; a++) {
StandbyDuration dur = lsDurations.get(a);
// proof if found duration starts after the end of current body.
if (dur != null && DateHelper.isDateAfter(DateHelper.getDateWithTime(date, dur.getValidFrom()),
stbyBody.getValidTo())) {
// set end time of current entry to the start of the next duration
stbyBody.setValidTo(DateHelper.getDateWithTime(date, dur.getValidFrom()));
stbyBody.setModifiedCause(TXT_AUTOMATIC_PLANNING_HOLIDAY);
this.createMsgExtendHoliday(stbyBody, date, true);
// end loop after first fitting result.
return stbyBody;
}
}
// no last duration for the day has been found. Therefore the start of day has
// been used for current entry
if (DateHelper.isDateAfter(DateHelper.getEndOfDay(date), stbyBody.getValidTo())) {
stbyBody.setValidTo(DateHelper.getEndOfDay(date));
stbyBody.setModifiedCause(TXT_AUTOMATIC_PLANNING_HOLIDAY);
this.createMsgExtendHoliday(stbyBody, date, true);
}
} else {
// no last duration for the day has been found. Therefore the start of day has
// been used for current entry
if (DateHelper.isDateAfter(DateHelper.getEndOfDay(date), stbyBody.getValidTo())) {
stbyBody.setValidTo(DateHelper.getEndOfDay(date));
stbyBody.setModifiedCause(TXT_AUTOMATIC_PLANNING_HOLIDAY);
this.createMsgExtendHoliday(stbyBody, date, true);
}
}
return stbyBody;
}
public PlanningBodyResultDto calculateScheduleBodyEntries(List<UserInStandbyGroup> lsUserInGroup, Date startDate,
Date currentDate, Date lastDate, Long lastTurnUserId, String username, Long lastStartUserId,
int phaseNumber) {
StandbyGroup group = lsUserInGroup.get(0).getStandbyGroup();
int currentPosition = this.getIndexOfLastPlannedUser(lastTurnUserId, lsUserInGroup);
if (currentPosition == -1) {
currentPosition = 0;
}
PlanningBodyResultDto resultObj = new PlanningBodyResultDto();
resultObj.setLastStartUserId(lastStartUserId);
resultObj.setNewPosition(currentPosition);
resultObj.setLastUserId(lsUserInGroup.get(currentPosition).getUser().getId());
// read fitting duration for the current phase.
List<StandbyDuration> lsFittingDurations = new ArrayList<>();
// BP-738 - check if durations are ending at start of phase.
if (phaseNumber == 1) {
this.appendEndingDurationsAtStartingPhase(lsFittingDurations, group, startDate, currentDate);
}
lsFittingDurations.addAll(this.getFittingDurationsOfGroup(group, currentDate));
if (!lsFittingDurations.isEmpty()) {
for (StandbyDuration fittingDuration : lsFittingDurations) {
int startWeekDayInt = DateHelper.getDayOfWeek(currentDate);
int endWeekDayInt = fittingDuration.getValidDayTo();
int coveredDays = DateHelper.calculateDifferenceOfDays(startWeekDayInt, endWeekDayInt,
fittingDuration.getValidFrom(),
new DateTime(fittingDuration.getValidTo()).minusSeconds(1).toDate());
if (coveredDays == 0) {
// single entry with no over night duration
LOGGER.debug("A day was found that just cover a single day.");
StandbyScheduleBody stbyBody = this.createScheduleBodyObject(group, username, currentDate,
lsUserInGroup.get(currentPosition).getUser(), fittingDuration, TXT_AUTOMATIC_PLANNING);
standbyScheduleBodyRepository.save(stbyBody);
// calculate if holiday
stbyBody = this.calculateHoliday(group, currentDate, stbyBody, true);
createMsgSavingBody(stbyBody);
} else {
resultObj = this.calculateMultiScheduleBodyEntries(lsUserInGroup, currentDate, lastDate,
currentPosition, username, fittingDuration, coveredDays);
}
resultObj.setCurrentDuration(fittingDuration);
PlanningBodyResultDto resultDto = this.resetCounterToNewPosition(currentPosition, lastStartUserId,
lsUserInGroup, group.getNextUserInNextCycle(), fittingDuration.getNextUserInNextDuration());
currentPosition = resultDto.getNewPosition();
lastStartUserId = resultDto.getLastStartUserId();
resultObj.setLastStartUserId(lastStartUserId);
}
resultObj.setNewPosition(currentPosition);
resultObj.setTempDate(DateHelper.addDaysToDate(currentDate, 1));
resultObj.setLastUserId(lsUserInGroup.get(currentPosition).getUser().getId());
} else {
resultObj.setTempDate(DateHelper.addDaysToDate(currentDate, 1));
// this.createMsgNotAvailable(group, currentDate);
}
return resultObj;
}
public PlanningBodyResultDto calculateMultiScheduleBodyEntries(List<UserInStandbyGroup> lsUserInGroup,
Date currentDate, Date lastDate, int currentPosition, String username, StandbyDuration fittingDuration,
int coveredDays) {
PlanningBodyResultDto resultObj = new PlanningBodyResultDto();
Date nextDay = null;
// entries with over night or many days duration
for (int i = 0; i < coveredDays; i++) {
nextDay = DateHelper.addDaysToDate(currentDate, 1);
if (DateHelper.isDateAfter(nextDay, lastDate) || DateHelper.isSameDate(nextDay, lastDate)) {
StandbyScheduleBody stbyBody = this.createScheduleBodyObject(lsUserInGroup.get(0).getStandbyGroup(),
username, currentDate, lsUserInGroup.get(currentPosition).getUser(), fittingDuration,
TXT_AUTOMATIC_PLANNING);
int dayOfCurrentWeek = DateHelper.getDayOfWeek(currentDate);
if (dayOfCurrentWeek != DateHelper.getDayOfWeek(new DateTime(lastDate).minusSeconds(1).toDate())
|| fittingDuration.getValidDayFrom() != dayOfCurrentWeek) {
// set start of day when it is not the start of the duration and it is not the
// end of the planning phase
stbyBody.setValidFrom(DateHelper.getStartOfDay(currentDate));
}
// reset ending date to beginning of next day.
stbyBody.setValidTo(DateHelper.getEndOfDay(currentDate));
standbyScheduleBodyRepository.save(stbyBody);
createMsgSavingBody(stbyBody);
String warning = "FĂ¼r das Datum (" + currentDate
+ ") wird der Eintrag nur bis zum Tages-Ende geplant, da der Folgetag den definierten Zeitraum der Phase Ă¼berschreitet.";
this.addMessageToList((new PlanningMsgDto(warning, PlanningMsgDto.INFO, PlanningMsgDto.CSS_INFO)));
resultObj.setNewPosition(currentPosition);
return resultObj;
} else {
List<StandbyScheduleBody> lsScheduleBodies = this.createOverlappingScheduleBodyObjects(
lsUserInGroup.get(0).getStandbyGroup(), username, currentDate,
lsUserInGroup.get(currentPosition).getUser(), fittingDuration, i, coveredDays);
for (StandbyScheduleBody stbyBody : lsScheduleBodies) {
standbyScheduleBodyRepository.save(stbyBody);
createMsgSavingBody(stbyBody);
resultObj.setTempDate(nextDay);
}
resultObj.setNewPosition(currentPosition);
resultObj.setTempDate(nextDay);
currentDate = resultObj.getTempDate();
}
}
return resultObj;
}
/**
* Method to create a success message for saving an {@link StandbyScheduleBody}
* object.
*
* @param stbyBody
* @return
*/
protected PlanningMsgDto createMsgSavingBody(StandbyScheduleBody stbyBody) {
String info = stbyBody.getUser().getFirstname() + " " + stbyBody.getUser().getLastname()
+ " wurde fĂ¼r den Zeitraum vom (" + stbyBody.getValidFrom() + ") bis zum (" + stbyBody.getValidTo()
+ ") eingeplant.";
PlanningMsgDto dto = new PlanningMsgDto(info, PlanningMsgDto.INFO, PlanningMsgDto.CSS_INFO_ML3);
this.addMessageToList(dto);
return dto;
}
/**
* Method to create a success message for saving an {@link StandbyScheduleBody}
* object.
*
* @param stbyBody
* @return
*/
protected PlanningMsgDto createMsgExtendHoliday(StandbyScheduleBody stbyBody, Date date, Boolean isExtendedDuty) {
StringBuilder strBuilder = new StringBuilder("Die Bereitschaftszeit fĂ¼r " + stbyBody.getUser().getFirstname()
+ " " + stbyBody.getUser().getLastname() + " muss am (" + date
+ ") auf Grund eines dienstfreien Tages");
if (isExtendedDuty) {
strBuilder.append(" verlängert werden.");
} else {
strBuilder.append(" vorgezogen werden.");
}
PlanningMsgDto dto = new PlanningMsgDto(strBuilder.toString(), PlanningMsgDto.INFO, PlanningMsgDto.CSS_INFO);
this.addMessageToList(dto);
return dto;
}
/**
* Method to create a warning because of no given group for planning.
*
* @param stbyBody
* @return
*/
protected PlanningMsgDto createMsgNotAvailable(StandbyGroup group, Date currentDate) {
String warning = "FĂ¼r die Gruppe (" + group.getTitle()
+ ") wurde keine beginnende Duration fĂ¼r folgenden Tag gefunden: (" + currentDate + ")";
LOGGER.warn(warning);
PlanningMsgDto dto = new PlanningMsgDto(warning, PlanningMsgDto.WARN, PlanningMsgDto.CSS_WARN);
this.addMessageToList(dto);
return dto;
}
/**
* Method to create a info message with the number of old deleted ScheduleBody
* objects.
*
* @param map
* @param blueprintDto
* @return
*/
protected PlanningMsgDto createMsgNumberOfPhases(Map<Integer, PlanningPhaseDto> map,
StandbyScheduleBlueprintDto blueprintDto) {
String info = "";
if (map == null || map.size() == 0) {
info = "Auf Grund fehlender Bereitschaften im gewählten Zeitraum, vom " + blueprintDto.getValidFrom()
+ " bis " + blueprintDto.getValidTo() + ", konnte keine Bereitschaftsphase generiert werden.";
LOGGER.info(info);
PlanningMsgDto dto = new PlanningMsgDto(info, PlanningMsgDto.WARN, PlanningMsgDto.CSS_WARN);
this.addMessageToList(dto);
return dto;
} else {
// search missing phase slots
this.addMessagesToList(this.createMsgMissingPhasesFound(map, blueprintDto));
info = "Durch wechselnde Gruppenmitglieder wurde(n) " + map.size()
+ " Bereitschaftsphase(n) fĂ¼r den angegebenen Zeitraum , vom " + blueprintDto.getValidFrom()
+ " bis " + blueprintDto.getValidTo() + ", erkannt.";
LOGGER.info(info);
PlanningMsgDto dto = new PlanningMsgDto(info, PlanningMsgDto.INFO, PlanningMsgDto.CSS_INFO);
this.addMessageToList(dto);
return dto;
}
}
protected List<PlanningMsgDto> createMsgStartOfPhase(Integer phaseId, PlanningPhaseDto phase) {
List<PlanningMsgDto> lsMsgDto = new ArrayList<>();
String info = "Die Bereitschaftsphase (" + phaseId + ") wird jetzt fĂ¼r den Zeitraum vom ("
+ DateHelper.getStartOfDay(phase.getStartDate()) + ") bis zum ("
+ DateHelper.getStartOfDay(phase.getEndDate()) + ") berechnet";
LOGGER.info(info);
PlanningMsgDto dto = new PlanningMsgDto(info, PlanningMsgDto.INFO, PlanningMsgDto.CSS_INFO);
this.addMessageToList(dto);
lsMsgDto.add(dto);
StringBuilder builder = new StringBuilder();
for (UserInStandbyGroup uisg : phase.getLsUsers()) {
User user = uisg.getUser();
String name = user.getFirstname() + " " + user.getLastname();
if (builder.toString().isEmpty()) {
builder.append(name);
} else {
builder.append(", " + name);
}
}
info = "Folgende Bereitschaftende werden verplant: " + builder.toString();
LOGGER.info(info);
dto = new PlanningMsgDto(info, PlanningMsgDto.INFO, PlanningMsgDto.CSS_INFO);
this.addMessageToList(dto);
lsMsgDto.add(dto);
return lsMsgDto;
}
protected List<PlanningMsgDto> createMsgMissingPhasesFound(Map<Integer, PlanningPhaseDto> map,
StandbyScheduleBlueprintDto blueprintDto) {
List<PlanningMsgDto> lsResults = new ArrayList<>();
Date validFromDate = DateHelper.getStartOfDay(blueprintDto.getValidFrom());
Date validToDate = DateHelper.getStartOfDay(blueprintDto.getValidTo());
Date currentDate = null;
Date lastDate = null;
for (Entry<Integer, PlanningPhaseDto> entry : map.entrySet()) {
currentDate = DateHelper.getStartOfDay(entry.getValue().getStartDate());
// set the validFromDate as lastDate for the first round
if (entry.getKey().intValue() == 1) {
lastDate = validFromDate;
}
if (!DateHelper.isSameDate(lastDate, currentDate)) {
String warn = "Es konnte keine Bereitschaftsphase vom (" + currentDate + ") bis zum (" + lastDate
+ ") berechnet werden, da es in der Bereitschaftsgruppe keine gĂ¼ltigen Mitarbeiter zur Auswahl gibt. "
+ "Der Plan kann somit fĂ¼r diesen Zeitraum nicht generiert werden.";
LOGGER.info(warn);
PlanningMsgDto dto = new PlanningMsgDto(warn, PlanningMsgDto.WARN, PlanningMsgDto.CSS_WARN);
lsResults.add(dto);
}
if (entry.getKey() == map.size()) {
Date endDate = DateHelper.getStartOfDay(entry.getValue().getEndDate());
if (DateHelper.isDateBefore(endDate, new DateTime(validToDate).minusDays(1).toDate())) {
String warn = "Es konnte keine Bereitschaftsphase vom (" + endDate + ") bis zum (" + validToDate
+ ") berechnet werden, da es in der Bereitschaftsgruppe keine gĂ¼ltigen Mitarbeiter zur Auswahl gibt. "
+ "Der Plan kann somit fĂ¼r diesen Zeitraum nicht generiert werden.";
LOGGER.info(warn);
PlanningMsgDto dto = new PlanningMsgDto(warn, PlanningMsgDto.WARN, PlanningMsgDto.CSS_WARN);
lsResults.add(dto);
}
}
// refresh last found date
lastDate = entry.getValue().getEndDate();
lastDate = DateHelper.getStartOfDay(lastDate);
}
return lsResults;
}
protected PlanningMsgDto createMsgEndOfPhase(Integer phasenId) {
String info = "Die Berechnung der Bereitschaftsphase (" + phasenId + ") wurde abgeschlossen";
LOGGER.info(info);
PlanningMsgDto dto = new PlanningMsgDto(info, PlanningMsgDto.INFO, PlanningMsgDto.CSS_INFO);
this.addMessageToList(dto);
return dto;
}
/**
* Method to create a info message with the number of old deleted ScheduleBody
* objects.
*
* @param stbyBody
* @return
*/
protected PlanningMsgDto createMsgLeaveOutStarter() {
String info = "Der letzte Starter wurde aufgrund des aktivierten Vorschubs Ă¼bersprungen.";
LOGGER.info(info);
PlanningMsgDto dto = new PlanningMsgDto(info, PlanningMsgDto.INFO, PlanningMsgDto.CSS_INFO);
this.addMessageToList(dto);
return dto;
}
/**
* Method to create a info message with the number of old deleted ScheduleBody
* objects.
*
* @param stbyBody
* @return
*/
protected PlanningMsgDto createMsgDeleteScheduleBodies(int numberOfElements) {
String warning = "Es wurden " + numberOfElements
+ " ältere Bereitschaften fĂ¼r den angegebenen Zeitraum entfernt.";
LOGGER.info(warning);
PlanningMsgDto dto = new PlanningMsgDto(warning, PlanningMsgDto.WARN, PlanningMsgDto.CSS_WARN);
this.addMessageToList(dto);
return dto;
}
/**
* Method to create a info message with the number of old deleted ScheduleBody
* objects.
*
* @param stbyBody
* @return
*/
protected PlanningMsgDto createMsgGroupLeaderChanged(User user, List<UserInStandbyGroup> lsUserInStandbyGroup) {
User nextUser = lsUserInStandbyGroup.get(0).getUser();
String warning = "Die ausgewählte Start - Bereitschaft (" + user.getFirstname() + " " + user.getLastname()
+ ") ist im angegebenen Zeitraum nicht aktiv. Es wird mit (" + nextUser.getFirstname() + " "
+ nextUser.getLastname() + ") begonnen.";
LOGGER.info(warning);
PlanningMsgDto dto = new PlanningMsgDto(warning, PlanningMsgDto.WARN, PlanningMsgDto.CSS_WARN);
this.addMessageToList(dto);
return dto;
}
/**
* Method to delete {@link StandbyScheduleBody} Objects by a list of id's.
*
* @param lsIds
* @return
*/
public PlanningMsgDto deleteScheduleBodiesByIds(List<Long> lsIds) throws SpException {
try {
for (Long id : lsIds) {
standbyScheduleBodyRepository.delete(id);
}
return createMsgDeleteScheduleBodies(lsIds.size());
} catch (Exception e) {
SpErrorEntry ee = SpExceptionEnum.COULD_NOT_DELETE_ENTITY_EXCEPTION.getEntry();
ee.setMessage(MessageFormat.format(ee.getMessage(), "StandbyScheduleBodies", e.getMessage()));
SpException spE = new SpException(ee);
LOGGER.error(spE, spE);
throw spE;
}
}
/**
* Method to check if date is a defined value in calendar database.
*
* @param date
* @return
*/
public Boolean isHoliday(Date date, StandbyGroup group) {
DateTime endTime = new DateTime(DateHelper.getEndOfDay(date));
endTime = endTime.minus(100);
List<CalendarDay> lsCalendarDays = calendarRepository.findValidByDate(DateHelper.getStartOfDay(date),
endTime.toDate());
if (lsCalendarDays != null && !lsCalendarDays.isEmpty()) {
List<CalendarDay> lsIgnoreList = group.getLsIgnoredCalendarDays();
for (CalendarDay day : lsCalendarDays) {
if (!lsIgnoreList.contains(day)) {
return true;
}
}
return false;
} else {
return false;
}
}
/**
* Method to get the start duration time for the group.
*
* @param group
* @param date
* @return null if no fitting duration is available
*/
public StandbyDuration getFittingDurationOfGroup(StandbyGroup group, Date date) {
List<StandbyDuration> lsDuration = this.getFittingDurationsOfGroup(group, date);
if (lsDuration != null && !lsDuration.isEmpty()) {
return lsDuration.get(0);
}
return null;
}
/**
* Method to get the start duration time for the group.
*
* @param group
* @param date
* @return null if no fitting duration is available
*/
public List<StandbyDuration> getFittingDurationsOfGroup(StandbyGroup group, Date date) {
int dayOfWeek = DateHelper.getDayOfWeek(date);
return standbyDurationRepository.findDurationForValidFromDay(group.getId(), dayOfWeek);
}
/**
* Method to get the start duration time for the group.
*
* @param group
* @param date
* @return null if no fitting duration is available
*/
public StandbyDuration getFittingDurationByValidTo(StandbyGroup group, Date date) {
List<StandbyDuration> lsDuration = this.getFittingDurationsByValidTo(group, date);
if (lsDuration != null && !lsDuration.isEmpty()) {
return lsDuration.get(lsDuration.size() - 1);
}
return null;
}
/**
* Method to get a list of {@link StandbyDuration} by its end time for the
* group.
*
* @param group
* @param date
* @return null if no fitting duration is available
*/
public List<StandbyDuration> getFittingDurationsByValidTo(StandbyGroup group, Date endDate) {
int dayOfWeek = DateHelper.getDayOfWeek(endDate);
return standbyDurationRepository.findDurationForValidToDay(group.getId(), dayOfWeek);
}
/**
* Method to get a list of {@link StandbyDuration} where the time is around the
* searched date. startOfDuration <= searchedDate <= endDate
*
* @param group
* @param date
* @return null if no fitting duration is available
*/
public List<StandbyDuration> getFittingDurationsAroundDate(StandbyGroup group, Date date) {
int dayOfWeek = DateHelper.getDayOfWeek(date);
List<StandbyDuration> lsFittingDurations = new ArrayList<>();
List<StandbyDuration> lsDurations = standbyDurationRepository.findById(group.getId());
for (StandbyDuration dur : lsDurations) {
if (dur.getValidDayFrom() <= dayOfWeek && dayOfWeek <= dur.getValidDayTo()) {
// start <= current <= end
lsFittingDurations.add(dur);
} else if (dur.getValidDayFrom() > dur.getValidDayTo()) {
// start > end
int calcValue = dayOfWeek;
if (dur.getValidDayFrom() > dayOfWeek) {
// AND start > current
calcValue = dayOfWeek + 7;
}
if (dur.getValidDayFrom() < calcValue && calcValue <= (dur.getValidDayTo() + 7)) {
lsFittingDurations.add(dur);
}
} else if (dur.getValidDayFrom() == dur.getValidDayTo() && (dur.getValidDayFrom() == dayOfWeek
|| DateHelper.calculateDifferenceOfDays(dur.getValidDayFrom(), dur.getValidDayTo(),
dur.getValidFrom(), dur.getValidTo()) == 7)) {
// if same day of week or if whole week
lsFittingDurations.add(dur);
}
}
return lsFittingDurations;
}
/**
* Method to set the currentPosition to 0 if end of list is reached.
*
* @param currentPosition
* @param listSize
* @return
*/
public PlanningBodyResultDto resetCounterToNewPosition(int currentPosition, Long lastStartUserId,
List<UserInStandbyGroup> lsUserInGroup, Boolean isNextUserInNextCycle, Boolean isNextUserInNextDuration) {
LOGGER.debug("currentPosition: " + currentPosition);
LOGGER.debug("lastStartUserId: " + lastStartUserId);
LOGGER.debug("UserList Size: " + lsUserInGroup.size());
// default values
PlanningBodyResultDto result = new PlanningBodyResultDto();
result.setLastStartUserId(lastStartUserId);
result.setNewPosition(currentPosition);
if (isNextUserInNextDuration) {
// get next available position
currentPosition = this.getNextAvailablePosition(currentPosition, lsUserInGroup);
LOGGER.debug("next currentPosition is: " + currentPosition);
int lastIndex = this.getIndexOfLastPlannedUser(lastStartUserId, lsUserInGroup);
LOGGER.debug("lastIndexOf: " + lastIndex);
if (currentPosition == lastIndex) {
LOGGER.debug("start user found at current position " + currentPosition);
if (isNextUserInNextCycle) {
LOGGER.debug("Because of isNextUserInNextCycle = '" + isNextUserInNextCycle
+ "' next position will be searched...) ");
this.createMsgLeaveOutStarter();
currentPosition = this.getNextAvailablePosition(currentPosition, lsUserInGroup);
LOGGER.debug("next currentPosition is: " + currentPosition);
Long nextStartUserId = lsUserInGroup.get(currentPosition).getUser().getId();
result.setLastStartUserId(nextStartUserId);
LOGGER.debug("Next start user id is set to :" + nextStartUserId.longValue());
} else {
LOGGER.debug("Because of isNextUserInNextCycle = '" + isNextUserInNextCycle
+ "' currentPosition is still: " + currentPosition);
}
}
} else {
LOGGER.info("isNextUserInNextDuration == false. CurrentUser still in charge.");
}
result.setNewPosition(currentPosition);
return result;
}
/**
* Method to calculate the next available position in array list. Starts with
* the first on if list has reached the end.
*
* @param currentPosition
* @param lsUserInGroup
* @return
*/
public int getNextAvailablePosition(int currentPosition, List<UserInStandbyGroup> lsUserInGroup) {
if (currentPosition + 1 < lsUserInGroup.size()) {
return currentPosition + 1;
} else {
return 0;
}
}
/**
* Method to get the position of the last planned user. Returns back '-1' if not
* found in given list.
*
* @param currentPosition
* @param listSize
* @return
*/
public int getIndexOfLastPlannedUser(Long lastStartUserId, List<UserInStandbyGroup> lsUserInGroup) {
int indexOf = -1;
for (UserInStandbyGroup userInGroup : lsUserInGroup) {
if (userInGroup.getUser().getId().longValue() == lastStartUserId.longValue()) {
indexOf = lsUserInGroup.indexOf(userInGroup);
return indexOf;
}
}
return indexOf;
}
/**
* Method to validate the input parameter for the planning.
*
* @param standbyBlueprintDto
* @return
* @throws SpException
*/
public boolean validateInputForPlanCalculation(StandbyScheduleBlueprintDto standbyBlueprintDto) throws SpException {
if (standbyBlueprintDto.getStandbyListId() != null) {
if (!standbyListRepository.exists(standbyBlueprintDto.getStandbyListId())) {
SpErrorEntry ee = SpExceptionEnum.UNKNOWN_ENTITY_EXCEPTION.getEntry();
String txt = MessageFormat.format(SpMsg.TXT_STANDBY_LIST_WITH_ID,
standbyBlueprintDto.getStandbyListId());
ee.setMessage(MessageFormat.format(ee.getMessage(), txt));
SpException spE = new SpException(ee);
LOGGER.error(spE, spE);
throw spE;
}
} else {
SpErrorEntry ee = SpExceptionEnum.MISSING_PARAMETER_EXCEPTION.getEntry();
ee.setMessage(MessageFormat.format(ee.getMessage(), "StandbyList"));
SpException spE = new SpException(ee);
LOGGER.error(spE, spE);
throw spE;
}
if (standbyBlueprintDto.getStandbyGroupId() != null) {
if (!standbyGroupRepository.exists(standbyBlueprintDto.getStandbyGroupId())) {
SpErrorEntry ee = SpExceptionEnum.UNKNOWN_ENTITY_EXCEPTION.getEntry();
String txt = MessageFormat.format(SpMsg.TXT_STANDBY_GROUP_WITH_ID,
standbyBlueprintDto.getStandbyGroupId());
ee.setMessage(MessageFormat.format(ee.getMessage(), txt));
SpException spE = new SpException(ee);
LOGGER.error(spE, spE);
throw spE;
}
} else {
SpErrorEntry ee = SpExceptionEnum.MISSING_PARAMETER_EXCEPTION.getEntry();
ee.setMessage(MessageFormat.format(ee.getMessage(), "StandbyGroup"));
SpException spE = new SpException(ee);
LOGGER.error(spE, spE);
throw spE;
}
if (standbyBlueprintDto.getValidFrom() == null) {
SpErrorEntry ee = SpExceptionEnum.MISSING_PARAMETER_EXCEPTION.getEntry();
ee.setMessage(MessageFormat.format(ee.getMessage(), "validFrom"));
SpException spE = new SpException(ee);
LOGGER.error(spE, spE);
throw spE;
}
if (standbyBlueprintDto.getValidTo() == null) {
SpErrorEntry ee = SpExceptionEnum.MISSING_PARAMETER_EXCEPTION.getEntry();
ee.setMessage(MessageFormat.format(ee.getMessage(), "validTo"));
SpException spE = new SpException(ee);
LOGGER.error(spE, spE);
throw spE;
}
return true;
}
/**
* Method to log the phase map object.
*
* @param resultMap
*/
public void logPhases(Map<Integer, PlanningPhaseDto> resultMap) {
LOGGER.debug("The following " + resultMap.keySet().size() + " had been found");
for (Entry<Integer, PlanningPhaseDto> entry : resultMap.entrySet()) {
LOGGER.debug("-----------------------");
LOGGER.debug("Phase :" + entry.getKey());
LOGGER.debug("-----------------------");
PlanningPhaseDto dto = entry.getValue();
LOGGER.debug("StartDate :" + dto.getStartDate());
LOGGER.debug("EndDate :" + dto.getEndDate());
LOGGER.debug("Number of StandbyUser :" + dto.getLsUsers().size());
for (UserInStandbyGroup uisg : dto.getLsUsers()) {
LOGGER.debug("Gruppenmitglied :" + uisg.getUser().getFirstname() + " " + uisg.getUser().getLastname());
}
}
}
/**
* Method to replace user in schedule bodies for the given interval
*
* @param actionDto
* @param username
* @return
* @throws SpException
*/
public PlanningMsgResponseDto replaceUserInPlan(StandbyScheduleActionDto actionDto, String username)
throws SpException {
PlanningMsgResponseDto responseDto = new PlanningMsgResponseDto();
this.startWithNewList();
// check input values
List<Object> lsInputParams = new ArrayList<>();
lsInputParams.add(actionDto.getCurrentUserId());
lsInputParams.add(actionDto.getNewUserId());
lsInputParams.add(actionDto.getStandbyGroupId());
lsInputParams.add(actionDto.getStatusId());
lsInputParams.add(actionDto.getValidFrom());
lsInputParams.add(actionDto.getValidTo());
lsInputParams.add(username);
ValidationHelper.isNoNullValueInList(lsInputParams);
try {
// history
archiveController.createArchiveOnReplaceInOneGroup(actionDto, username);
User currentUser = userRepository.findOne(actionDto.getCurrentUserId());
User newUser = userRepository.findOne(actionDto.getNewUserId());
StandbyGroup standbyGroup = standbyGroupRepository.findOne(actionDto.getStandbyGroupId());
Date from = actionDto.getValidFrom();
Date to = actionDto.getValidTo();
// mail
mailing.replaceMessage1(actionDto, currentUser, newUser, standbyGroup);
mailing.replaceMessage2(actionDto, currentUser, newUser, standbyGroup);
ArrayList<StandbyGroup> lsStandbyGroups = new ArrayList<>();
List<StandbyScheduleBody> lsBodies = standbyScheduleBodyRepository.findByUserAndGroupAndDateAndStatus(
currentUser.getId(), actionDto.getStandbyGroupId(), DateHelper.getStartOfDay(from),
DateHelper.getEndOfDay(to), actionDto.getStatusId());
for (StandbyScheduleBody body : lsBodies) {
this.splitScheduleBody(body, from, to, newUser, username);
if (!lsStandbyGroups.contains(body.getStandbyGroup())) {
lsStandbyGroups.add(body.getStandbyGroup());
}
}
// start validation
List<String> lsValidationBeanNames = new ArrayList<>();
lsValidationBeanNames.add(ValidationController.BEAN_USER_AVAILABLE_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_USER_AVAILABLE_FOR_GROUP_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_GROUP_COVERAGE_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_DOUBLE_PLANNED_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_PLACEHOLDER_STANDBY_USER_VALIDATOR);
responseDto.getLsMsg()
.addAll(validatonController.startValidation(actionDto.getValidFrom(), actionDto.getValidTo(),
lsStandbyGroups, lsValidationBeanNames, actionDto.getStatusId(), actionDto.getNewUserId()));
return responseDto;
} catch (Exception e) {
LOGGER.error(e, e);
SpErrorEntry ee = SpExceptionEnum.DEFAULT_EXCEPTION.getEntry();
ee.setE(e);
throw new SpException(ee);
}
}
protected Boolean splitScheduleBody(StandbyScheduleBody body, Date from, Date to, User newUser, String modifiedBy)
throws SpException {
try {
Date tmpFrom = from;
Date tmpTo = to;
if (DateHelper.isDateBefore(from, DateHelper.getStartOfDay(body.getValidFrom()))) {
tmpFrom = DateHelper.getStartOfDay(body.getValidFrom());
}
if (DateHelper.isDateAfter(to, DateHelper.getEndOfDay(body.getValidFrom()))) {
tmpTo = DateHelper.getEndOfDay(body.getValidFrom());
}
// interval of ui params
Interval intervalSelection = new Interval(new DateTime(tmpFrom).getMillis(),
new DateTime(tmpTo).getMillis());
// interval of saved schedule body
Interval intervalBody = new Interval(new DateTime(body.getValidFrom()).getMillis(),
new DateTime(body.getValidTo()).getMillis());
// overlapping interval between params and schedule body values
Interval intervalOverlap = intervalBody.overlap(intervalSelection);
if (intervalOverlap != null) {
int isSameStart = DateHelper.isSameDateTime(body.getValidFrom(), tmpFrom);
int isSameEnd = DateHelper.isSameDateTime(body.getValidTo(), tmpTo);
tmpFrom = intervalOverlap.getStart().toDate();
tmpTo = intervalOverlap.getEnd().toDate();
if (isSameStart >= 0) {
if (isSameEnd == 0) {
// full replace
body.setUser(newUser);
body.setModifiedCause(TXT_MANUAL_CHANGE);
body.setModificationDate(new Date());
body.setModifiedBy(modifiedBy);
standbyScheduleBodyRepository.save(body);
} else if (isSameEnd < 0) {
body.setUser(newUser);
body.setModifiedCause(TXT_MANUAL_CHANGE);
body.setModificationDate(new Date());
body.setModifiedBy(modifiedBy);
body.setValidTo(tmpTo);
standbyScheduleBodyRepository.save(body);
} else {
StandbyScheduleBody newBody = body.copy();
newBody.setUser(newUser);
newBody.setModifiedCause(TXT_MANUAL_CHANGE);
newBody.setModificationDate(new Date());
newBody.setModifiedBy(modifiedBy);
newBody.setValidTo(tmpTo);
standbyScheduleBodyRepository.save(newBody);
body.setValidFrom(tmpTo);
body.setModifiedCause(TXT_MANUAL_CHANGE);
body.setModificationDate(new Date());
body.setModifiedBy(modifiedBy);
standbyScheduleBodyRepository.save(body);
}
} else if (isSameEnd == 0) {
StandbyScheduleBody newBody = body.copy();
newBody.setUser(newUser);
newBody.setModifiedCause(TXT_MANUAL_CHANGE);
newBody.setModificationDate(new Date());
newBody.setModifiedBy(modifiedBy);
newBody.setValidFrom(tmpFrom);
standbyScheduleBodyRepository.save(newBody);
body.setValidTo(tmpFrom);
body.setModifiedCause(TXT_MANUAL_CHANGE);
body.setModificationDate(new Date());
body.setModifiedBy(modifiedBy);
standbyScheduleBodyRepository.save(body);
} else if (isSameEnd < 0) {
// replace with end at 23:59
StandbyScheduleBody newBody = body.copy();
newBody.setUser(newUser);
newBody.setModifiedCause(TXT_MANUAL_CHANGE);
newBody.setModificationDate(new Date());
newBody.setModifiedBy(modifiedBy);
newBody.setValidFrom(tmpFrom);
newBody.setValidTo(tmpTo);
standbyScheduleBodyRepository.save(newBody);
body.setModifiedCause(TXT_MANUAL_CHANGE);
body.setModificationDate(new Date());
body.setModifiedBy(modifiedBy);
body.setValidTo(tmpFrom);
standbyScheduleBodyRepository.save(body);
} else {
// set new user (n1) between old (o1) existing entry by splitting it.
// Looks like this [o1--n1--o2]
StandbyScheduleBody newBody = body.copy();
newBody.setUser(newUser);
newBody.setModifiedCause(TXT_MANUAL_CHANGE);
newBody.setModificationDate(new Date());
newBody.setModifiedBy(modifiedBy);
newBody.setValidFrom(tmpFrom);
newBody.setValidTo(tmpTo);
standbyScheduleBodyRepository.save(newBody);
// set new entry to not loose the time after
StandbyScheduleBody oldBodyAfter = body.copy();
oldBodyAfter.setModifiedCause(TXT_MANUAL_CHANGE);
oldBodyAfter.setModificationDate(new Date());
oldBodyAfter.setModifiedBy(modifiedBy);
oldBodyAfter.setValidFrom(tmpTo);
standbyScheduleBodyRepository.save(oldBodyAfter);
// set
body.setModifiedCause(TXT_MANUAL_CHANGE);
body.setModificationDate(new Date());
body.setModifiedBy(modifiedBy);
body.setValidTo(tmpFrom);
standbyScheduleBodyRepository.save(body);
}
}
return true;
} catch (Exception e) {
LOGGER.error(e, e);
SpErrorEntry ee = SpExceptionEnum.DEFAULT_EXCEPTION.getEntry();
ee.setE(e);
throw new SpException(ee);
}
}
/**
* Method to change the time of a schedule body.
*
* @param actionDto
* @param username
* @return
* @throws SpException
*/
public PlanningMsgResponseDto moveUserInPlan(StandbyScheduleActionDto actionDto, String username)
throws SpException {
PlanningMsgResponseDto responseDto = new PlanningMsgResponseDto();
this.startWithNewList();
// check input values
List<Object> lsInputParams = new ArrayList<>();
lsInputParams.add(actionDto.getNewUserId());
lsInputParams.add(actionDto.getScheduleBodyId());
lsInputParams.add(actionDto.getValidFrom());
lsInputParams.add(actionDto.getValidTo());
lsInputParams.add(actionDto.getStandbyGroupId());
lsInputParams.add(username);
ValidationHelper.isNoNullValueInList(lsInputParams);
StandbyGroup tempOldGroup = null;
StandbyGroup tempNewGroup = null;
try {
// history
archiveController.createArchiveOnMove(actionDto, username);
StandbyScheduleBody body = standbyScheduleBodyRepository.findOne(actionDto.getScheduleBodyId());
tempNewGroup = standbyGroupRepository.findOne(actionDto.getStandbyGroupId());
// mail
mailing.moveMessage1(actionDto, body, tempNewGroup);
tempOldGroup = body.getStandbyGroup();
body.setModifiedCause(SpMsg.ACT_STANDBY_MOVE);
body.setModificationDate(new Date());
body.setModifiedBy(username);
body.setStandbyGroup(tempNewGroup);
DateTime dt = new DateTime(actionDto.getValidTo());
if (DateHelper.isSameDate(actionDto.getValidFrom(), dt.minusSeconds(1).toDate())) {
// because of no overlapping day schedule body just
// gets new values, no second body is needed.
body.setValidFrom(actionDto.getValidFrom());
body.setValidTo(actionDto.getValidTo());
standbyScheduleBodyRepository.save(body);
} else {
Date nextDate = actionDto.getValidFrom();
StandbyScheduleBody newBody = null;
while (DateHelper.isDateBefore(nextDate, actionDto.getValidTo())
&& !DateHelper.isSameDate(nextDate, actionDto.getValidTo())) {
newBody = body.copy();
if (DateHelper.isSameDate(actionDto.getValidFrom(), nextDate)) {
// do not change start time on first day
newBody.setValidFrom(nextDate);
} else {
// begin with = 0:00 if not first day
newBody.setValidFrom(DateHelper.getStartOfDay(nextDate));
}
newBody.setValidTo(DateHelper.getEndOfDay(nextDate));
newBody.setModifiedCause(TXT_MANUAL_CHANGE);
newBody.setModificationDate(new Date());
newBody.setModifiedBy(username);
newBody.setStandbyGroup(body.getStandbyGroup());
standbyScheduleBodyRepository.save(newBody);
nextDate = DateHelper.addDaysToDate(nextDate, 1);
}
// add last day
StandbyScheduleBody lastBody = body.copy();
lastBody.setValidFrom(DateHelper.getStartOfDay(actionDto.getValidTo()));
lastBody.setValidTo(actionDto.getValidTo());
lastBody.setModifiedCause(TXT_MANUAL_CHANGE);
lastBody.setModificationDate(new Date());
lastBody.setModifiedBy(username);
lastBody.setStandbyGroup(body.getStandbyGroup());
standbyScheduleBodyRepository.save(lastBody);
// remove original because of splitting it to multiple entries
standbyScheduleBodyRepository.delete(body.getId());
}
ArrayList<StandbyGroup> lsStandbyGroups = new ArrayList<>();
lsStandbyGroups.add(tempOldGroup);
// add new group to check list if it differs from origin group
if (tempOldGroup.getId().longValue() != tempNewGroup.getId().longValue()) {
lsStandbyGroups.add(tempNewGroup);
}
// start validation
List<String> lsValidationBeanNames = new ArrayList<>();
lsValidationBeanNames.add(ValidationController.BEAN_USER_AVAILABLE_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_USER_AVAILABLE_FOR_GROUP_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_GROUP_COVERAGE_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_DOUBLE_PLANNED_VALIDATOR);
lsValidationBeanNames.add(ValidationController.BEAN_PLACEHOLDER_STANDBY_USER_VALIDATOR);
responseDto.getLsMsg()
.addAll(validatonController.startValidation(actionDto.getValidFrom(), actionDto.getValidTo(),
lsStandbyGroups, lsValidationBeanNames, actionDto.getStatusId(), actionDto.getNewUserId()));
return responseDto;
} catch (Exception e) {
LOGGER.error(e, e);
SpErrorEntry ee = SpExceptionEnum.DEFAULT_EXCEPTION.getEntry();
ee.setE(e);
throw new SpException(ee);
}
}
/**
* Method to search for durations that will end at the day the current phase is
* starting. Therefore maybe the end of durations that are starting the day
* before will be found.
*
* @param lsFittingDurations
* @param group
* @param startDate
* @param currentDate
* @return
*/
private List<StandbyDuration> appendEndingDurationsAtStartingPhase(List<StandbyDuration> lsFittingDurations,
StandbyGroup group, Date startDate, Date currentDate) {
// Only at the start of a new phase the ending durations have to be used, too.
if (startDate.equals(currentDate)) {
List<StandbyDuration> lsFittingDurationsEndingAtStart = this.getFittingDurationsAroundDate(group,
currentDate);
if (lsFittingDurationsEndingAtStart != null && !lsFittingDurationsEndingAtStart.isEmpty()) {
for (StandbyDuration dur : lsFittingDurationsEndingAtStart) {
int currentWeekDayInt = DateHelper.getDayOfWeek(currentDate);
int startDurationWeekDayInt = dur.getValidDayFrom();
// do not use durations that start at the current day because they will be found
// in the normal calculation.
if (startDurationWeekDayInt != currentWeekDayInt) {
StandbyDuration tmpDuration = dur.copy();
// set start to current day 00:00 because duration comes from day earlier.
tmpDuration.setValidFrom(DateHelper.getStartOfDay(tmpDuration.getValidTo()));
tmpDuration.setValidDayFrom(DateHelper.getDayOfWeek(currentDate));
int endDurationWeekDayInt = dur.getValidDayTo();
if (endDurationWeekDayInt == currentWeekDayInt) {
// if durations ends current day use the end of duration
tmpDuration.setValidDayTo(DateHelper.getDayOfWeek(currentDate));
} else {
// else use the end of day.
tmpDuration.setValidDayTo(DateHelper.getDayOfWeek(currentDate));
tmpDuration.setValidTo(DateHelper.getEndOfDay(currentDate));
}
lsFittingDurations.add(tmpDuration);
}
}
}
}
return lsFittingDurations;
}
/**
* Method to add PlanningMsgDtos to list and stop filling the list at an amount
* bigger then 200 entries.
*/
public void addMessagesToList(List<PlanningMsgDto> lsMsg) {
for (PlanningMsgDto dto : lsMsg) {
if (dto.getType().equals(PlanningMsgDto.INFO)) {
lsPlanningMsg.add(dto);
} else if (warningCounter < 200) {
lsPlanningMsg.add(dto);
warningCounter++;
} else {
PlanningMsgDto lastDto = lsPlanningMsg.get(lsPlanningMsg.size() - 1);
if (!lastDto.getMsg().equals(TXT_AND_MORE)) {
lsPlanningMsg.add(new PlanningMsgDto(TXT_AND_MORE, PlanningMsgDto.WARN, PlanningMsgDto.CSS_WARN));
}
}
}
}
/**
* Method to add a PlanningMsgDto to list.
*/
public void addMessageToList(PlanningMsgDto msg) {
List<PlanningMsgDto> lsMsg = new ArrayList<>();
lsMsg.add(msg);
this.addMessagesToList(lsMsg);
}
public List<PlanningMsgDto> getLsMsgDto() {
return lsPlanningMsg;
}
public void startWithNewList() {
lsPlanningMsg = new ArrayList<>();
warningCounter = 0;
}
public int getWarningCounter() {
return warningCounter;
}
}