package org.synyx.urlaubsverwaltung.web.person;
import org.joda.time.DateMidnight;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.synyx.urlaubsverwaltung.core.account.domain.Account;
import org.synyx.urlaubsverwaltung.core.account.domain.VacationDaysLeft;
import org.synyx.urlaubsverwaltung.core.account.service.AccountService;
import org.synyx.urlaubsverwaltung.core.account.service.VacationDaysService;
import org.synyx.urlaubsverwaltung.core.department.Department;
import org.synyx.urlaubsverwaltung.core.department.DepartmentService;
import org.synyx.urlaubsverwaltung.core.person.Person;
import org.synyx.urlaubsverwaltung.core.person.PersonService;
import org.synyx.urlaubsverwaltung.core.person.Role;
import org.synyx.urlaubsverwaltung.core.settings.FederalState;
import org.synyx.urlaubsverwaltung.core.settings.SettingsService;
import org.synyx.urlaubsverwaltung.core.util.DateUtil;
import org.synyx.urlaubsverwaltung.core.workingtime.WorkingTime;
import org.synyx.urlaubsverwaltung.core.workingtime.WorkingTimeService;
import org.synyx.urlaubsverwaltung.security.SecurityRules;
import org.synyx.urlaubsverwaltung.security.SessionService;
import org.synyx.urlaubsverwaltung.web.ControllerConstants;
import org.synyx.urlaubsverwaltung.web.department.DepartmentConstants;
import org.synyx.urlaubsverwaltung.web.department.UnknownDepartmentException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* Controller for management of {@link Person} entities.
*
* @author Aljona Murygina
*/
@Controller
@RequestMapping("/web")
public class PersonController {
@Autowired
private PersonService personService;
@Autowired
private AccountService accountService;
@Autowired
private VacationDaysService vacationDaysService;
@Autowired
private DepartmentService departmentService;
@Autowired
private WorkingTimeService workingTimeService;
@Autowired
private SettingsService settingsService;
@Autowired
private SessionService sessionService;
@RequestMapping(value = "/staff/{personId}", method = RequestMethod.GET)
public String showStaffInformation(@PathVariable("personId") Integer personId,
@RequestParam(value = ControllerConstants.YEAR_ATTRIBUTE, required = false) Optional<Integer> requestedYear,
Model model) throws UnknownPersonException, AccessDeniedException {
Person person = personService.getPersonByID(personId).orElseThrow(() -> new UnknownPersonException(personId));
Person signedInUser = sessionService.getSignedInUser();
if (!sessionService.isSignedInUserAllowedToAccessPersonData(signedInUser, person)) {
throw new AccessDeniedException(String.format(
"User '%s' has not the correct permissions to access data of user '%s'",
signedInUser.getLoginName(), person.getLoginName()));
}
Integer year = requestedYear.isPresent() ? requestedYear.get() : DateMidnight.now().getYear();
model.addAttribute(ControllerConstants.YEAR_ATTRIBUTE, year);
model.addAttribute(PersonConstants.PERSON_ATTRIBUTE, person);
model.addAttribute(DepartmentConstants.DEPARTMENTS_ATTRIBUTE,
departmentService.getAssignedDepartmentsOfMember(person));
Optional<WorkingTime> workingTime = workingTimeService.getCurrentOne(person);
Optional<FederalState> optionalFederalState = Optional.empty();
if (workingTime.isPresent()) {
model.addAttribute("workingTime", workingTime.get());
optionalFederalState = workingTime.get().getFederalStateOverride();
}
if (optionalFederalState.isPresent()) {
model.addAttribute("federalState", optionalFederalState.get());
} else {
model.addAttribute("federalState",
settingsService.getSettings().getWorkingTimeSettings().getFederalState());
}
Optional<Account> account = accountService.getHolidaysAccount(year, person);
if (account.isPresent()) {
model.addAttribute("account", account.get());
}
return PersonConstants.PERSON_DETAIL_JSP;
}
@PreAuthorize(SecurityRules.IS_PRIVILEGED_USER)
@RequestMapping(value = "/staff", method = RequestMethod.GET)
public String showStaff() {
return "redirect:/web/staff?active=true";
}
@PreAuthorize(SecurityRules.IS_PRIVILEGED_USER)
@RequestMapping(value = "/staff", method = RequestMethod.GET, params = "active")
public String showStaff(@RequestParam(value = "active") Boolean active,
@RequestParam(value = ControllerConstants.DEPARTMENT_ATTRIBUTE, required = false) Optional<Integer> requestedDepartmentId,
@RequestParam(value = ControllerConstants.YEAR_ATTRIBUTE, required = false) Optional<Integer> requestedYear,
Model model) throws UnknownDepartmentException {
Integer year = requestedYear.isPresent() ? requestedYear.get() : DateMidnight.now().getYear();
Person signedInUser = sessionService.getSignedInUser();
final List<Person> persons = active ? getRelevantActivePersons(signedInUser)
: getRelevantInactivePersons(signedInUser);
if (requestedDepartmentId.isPresent()) {
Integer departmentId = requestedDepartmentId.get();
Department department = departmentService.getDepartmentById(departmentId).orElseThrow(() ->
new UnknownDepartmentException(departmentId));
// if department filter is active, only department members are relevant
persons.retainAll(department.getMembers());
model.addAttribute(ControllerConstants.DEPARTMENT_ATTRIBUTE, department);
}
prepareStaffView(signedInUser, persons, year, model);
return PersonConstants.STAFF_JSP;
}
private List<Person> getRelevantActivePersons(Person signedInUser) {
if (signedInUser.hasRole(Role.BOSS) || signedInUser.hasRole(Role.OFFICE)) {
return personService.getActivePersons();
}
// NOTE: If the signed in user is only department head, he wants to see only the persons of his departments
if (signedInUser.hasRole(Role.DEPARTMENT_HEAD)) {
List<Person> members = departmentService.getManagedMembersOfDepartmentHead(signedInUser);
// NOTE: Only persons without inactive role are relevant
return members.stream().filter(person -> !person.hasRole(Role.INACTIVE)).collect(Collectors.toList());
}
// NOTE: If the signed in user is second stage authority, he wants to see only the persons of his departments
if (signedInUser.hasRole(Role.SECOND_STAGE_AUTHORITY)) {
List<Person> members = departmentService.getMembersForSecondStageAuthority(signedInUser);
// NOTE: Only persons without inactive role are relevant
return members.stream().filter(person -> !person.hasRole(Role.INACTIVE)).collect(Collectors.toList());
}
return Collections.<Person>emptyList();
}
private List<Person> getRelevantInactivePersons(Person signedInUser) {
if (signedInUser.hasRole(Role.BOSS) || signedInUser.hasRole(Role.OFFICE)) {
return personService.getInactivePersons();
}
// NOTE: If the signed in user is only department head, he wants to see only the persons of his departments
if (signedInUser.hasRole(Role.DEPARTMENT_HEAD)) {
List<Person> members = departmentService.getManagedMembersOfDepartmentHead(signedInUser);
// NOTE: Only persons with inactive role are relevant
return members.stream().filter(person -> person.hasRole(Role.INACTIVE)).collect(Collectors.toList());
}
// NOTE: If the signed in user is second stage authority, he wants to see only the persons of his departments
if (signedInUser.hasRole(Role.SECOND_STAGE_AUTHORITY)) {
List<Person> members = departmentService.getMembersForSecondStageAuthority(signedInUser);
// NOTE: Only persons with inactive role are relevant
return members.stream().filter(person -> person.hasRole(Role.INACTIVE)).collect(Collectors.toList());
}
return Collections.<Person>emptyList();
}
private List<Department> getRelevantDepartments(Person signedInUser) {
if (signedInUser.hasRole(Role.BOSS) || signedInUser.hasRole(Role.OFFICE)) {
return departmentService.getAllDepartments();
}
// NOTE: If the signed in user is only department head, he wants to see only the persons of his departments
if (signedInUser.hasRole(Role.DEPARTMENT_HEAD)) {
return departmentService.getManagedDepartmentsOfDepartmentHead(signedInUser);
}
// NOTE: If the signed in user is second stage authority, he wants to see only the persons of his departments
if (signedInUser.hasRole(Role.SECOND_STAGE_AUTHORITY)) {
return departmentService.getManagedDepartmentsOfSecondStageAuthority(signedInUser);
}
// normal users can see their own departments only
return departmentService.getAssignedDepartmentsOfMember(signedInUser);
}
private void prepareStaffView(Person signedInUser, List<Person> persons, int year, Model model) {
Map<Person, Account> accounts = new HashMap<>();
Map<Person, VacationDaysLeft> vacationDaysLeftMap = new HashMap<>();
for (Person person : persons) {
// get person's account
Optional<Account> account = accountService.getHolidaysAccount(year, person);
if (account.isPresent()) {
Account holidaysAccount = account.get();
accounts.put(person, holidaysAccount);
vacationDaysLeftMap.put(person, vacationDaysService.getVacationDaysLeft(holidaysAccount));
}
}
model.addAttribute(PersonConstants.PERSONS_ATTRIBUTE, persons);
model.addAttribute("accounts", accounts);
model.addAttribute("vacationDaysLeftMap", vacationDaysLeftMap);
model.addAttribute(PersonConstants.BEFORE_APRIL_ATTRIBUTE, DateUtil.isBeforeApril(DateMidnight.now()));
model.addAttribute(ControllerConstants.YEAR_ATTRIBUTE, year);
model.addAttribute("now", DateMidnight.now());
List<Department> departments = getRelevantDepartments(signedInUser);
Collections.sort(departments, (a, b) -> a.getName().compareTo(b.getName()));
model.addAttribute("departments", departments);
}
}