Repository: urlaubsverwaltung/urlaubsverwaltung (java)

Issue:
Als Benutzer möchte ich die Abwesenheitsstatistik für meine Informationen einsehen können
Als Mitarbeitende Person ohne weitere Berechtigungen, möchte ich die Abwesenheitsstatistik unter Abwesenheiten für mich einsehen können, sodass ich auf einen Blick alle Informationen habe.

Pull request:
Absence statistics for normal user
closes #3106

<!--

Thanks for contributing to the Urlaubsverwaltung.
Please review the following notes before submitting you pull request.

Please look for other issues or pull requests which already work on this topic. Is somebody already on it? Do you need to synchronize?

# Security Vulnerabilities

🛑 STOP! 🛑 If your contribution fixes a security vulnerability, please do not submit it.
Instead, please open a report of a security vulnerability via
[Report a security vulnerability](https://github.com/urlaubsverwaltung/urlaubsverwaltung/security/advisories/new)


# Describing Your Changes

If, having reviewed the notes above, you're ready to submit your pull request, please
provide a brief description of the proposed changes.

If they:
 🐞 fix a bug, please describe the broken behaviour and how the changes fix it.
    Please label with 'type: bug' and 'status: new'
    
 🎁 make an enhancement, please describe the new functionality and why you believe it's useful.
    Please label with 'type: enhancement' and 'status: new'
 
If your pull request relates to any existing issues,
please reference them by using the issue number prefixed with #.

-->


Patch:
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveDetailsViewController.java b/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveDetailsViewController.java
index a39c80b50..034e5f1e2 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveDetailsViewController.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveDetailsViewController.java
@@ -119,12 +119,13 @@ class ApplicationForLeaveDetailsViewController implements HasLaunchpad {
     }
 
     @GetMapping("/{applicationId}")
-    public String showApplicationDetail(@PathVariable("applicationId") Long applicationId,
-                                        @RequestParam(value = "year", required = false) Integer requestedYear,
-                                        @RequestParam(value = "action", required = false) String action,
-                                        @RequestParam(value = "shortcut", required = false) boolean shortcut,
-                                        Model model, Locale locale)
-        throws UnknownApplicationForLeaveException {
+    public String showApplicationDetail(
+        @PathVariable("applicationId") Long applicationId,
+        @RequestParam(value = "year", required = false) Integer requestedYear,
+        @RequestParam(value = "action", required = false) String action,
+        @RequestParam(value = "shortcut", required = false, defaultValue = "false") boolean shortcut,
+        Model model, Locale locale
+    ) throws UnknownApplicationForLeaveException {
 
         final Application application = applicationService.getApplicationById(applicationId)
             .orElseThrow(() -> new UnknownApplicationForLeaveException(applicationId));
@@ -148,10 +149,12 @@ class ApplicationForLeaveDetailsViewController implements HasLaunchpad {
      */
     @PreAuthorize(IS_BOSS_OR_DEPARTMENT_HEAD_OR_SECOND_STAGE_AUTHORITY)
     @PostMapping("/{applicationId}/allow")
-    public String allowApplication(@PathVariable("applicationId") Long applicationId,
-                                   @ModelAttribute("comment") ApplicationCommentForm comment, Errors errors,
-                                   @RequestParam(value = "redirect", required = false) String redirectUrl,
-                                   RedirectAttributes redirectAttributes) throws UnknownApplicationForLeaveException {
+    public String allowApplication(
+        @PathVariable("applicationId") Long applicationId,
+        @ModelAttribute("comment") ApplicationCommentForm comment, Errors errors,
+        @RequestParam(value = "redirect", required = false) String redirectUrl,
+        RedirectAttributes redirectAttributes
+    ) throws UnknownApplicationForLeaveException {
 
         final Application application = applicationService.getApplicationById(applicationId)
             .orElseThrow(() -> new UnknownApplicationForLeaveException(applicationId));
@@ -203,9 +206,11 @@ class ApplicationForLeaveDetailsViewController implements HasLaunchpad {
      */
     @PreAuthorize(IS_PRIVILEGED_USER)
     @PostMapping("/{applicationId}/refer")
-    public String referApplication(@PathVariable("applicationId") Long applicationId,
-                                   @ModelAttribute("referredPerson") ReferredPerson referredPerson, RedirectAttributes redirectAttributes)
-        throws UnknownApplicationForLeaveException, UnknownPersonException {
+    public String referApplication(
+        @PathVariable("applicationId") Long applicationId,
+        @ModelAttribute("referredPerson") ReferredPerson referredPerson,
+        RedirectAttributes redirectAttributes
+    ) throws UnknownApplicationForLeaveException, UnknownPersonException {
 
         final Application application = applicationService.getApplicationById(applicationId)
             .orElseThrow(() -> new UnknownApplicationForLeaveException(applicationId));
@@ -233,10 +238,12 @@ class ApplicationForLeaveDetailsViewController implements HasLaunchpad {
 
     @PreAuthorize(IS_BOSS_OR_DEPARTMENT_HEAD_OR_SECOND_STAGE_AUTHORITY)
     @PostMapping("/{applicationId}/reject")
-    public String rejectApplication(@PathVariable("applicationId") Long applicationId,
-                                    @ModelAttribute("comment") ApplicationCommentForm comment, Errors errors,
-                                    @RequestParam(value = "redirect", required = false) String redirectUrl,
-                                    RedirectAttributes redirectAttributes) throws UnknownApplicationForLeaveException {
+    public String rejectApplication(
+        @PathVariable("applicationId") Long applicationId,
+        @ModelAttribute("comment") ApplicationCommentForm comment, Errors errors,
+        @RequestParam(value = "redirect", required = false) String redirectUrl,
+        RedirectAttributes redirectAttributes
+    ) throws UnknownApplicationForLeaveException {
 
         final Application application = applicationService.getApplicationById(applicationId)
             .orElseThrow(() -> new UnknownApplicationForLeaveException(applicationId));
@@ -281,10 +288,11 @@ class ApplicationForLeaveDetailsViewController implements HasLaunchpad {
      * Cancelling an application for leave on behalf for someone is allowed only for Office.
      */
     @PostMapping("/{applicationId}/cancel")
-    public String cancelApplication(@PathVariable("applicationId") Long applicationId,
-                                    @ModelAttribute("comment") ApplicationCommentForm comment, Errors errors,
-                                    RedirectAttributes redirectAttributes)
-        throws UnknownApplicationForLeaveException {
+    public String cancelApplication(
+        @PathVariable("applicationId") Long applicationId,
+        @ModelAttribute("comment") ApplicationCommentForm comment, Errors errors,
+        RedirectAttributes redirectAttributes
+    ) throws UnknownApplicationForLeaveException {
 
         final Application application = applicationService.getApplicationById(applicationId)
             .orElseThrow(() -> new UnknownApplicationForLeaveException(applicationId));
@@ -328,10 +336,11 @@ class ApplicationForLeaveDetailsViewController implements HasLaunchpad {
      * Cancel the cancellation request of an application for leave.
      */
     @PostMapping("/{applicationId}/decline-cancellation-request")
-    public String declineCancellationRequestApplication(@PathVariable("applicationId") Long applicationId,
-                                                        @ModelAttribute("comment") ApplicationCommentForm comment, Errors errors,
-                                                        RedirectAttributes redirectAttributes)
-        throws UnknownApplicationForLeaveException {
+    public String declineCancellationRequestApplication(
+        @PathVariable("applicationId") Long applicationId,
+        @ModelAttribute("comment") ApplicationCommentForm comment, Errors errors,
+        RedirectAttributes redirectAttributes
+    ) throws UnknownApplicationForLeaveException {
 
         final Application application = applicationService.getApplicationById(applicationId)
             .orElseThrow(() -> new UnknownApplicationForLeaveException(applicationId));
@@ -363,8 +372,10 @@ class ApplicationForLeaveDetailsViewController implements HasLaunchpad {
      * Remind the bosses about the decision of an application for leave.
      */
     @PostMapping("/{applicationId}/remind")
-    public String remindBoss(@PathVariable("applicationId") Long applicationId,
-                             RedirectAttributes redirectAttributes) throws UnknownApplicationForLeaveException {
+    public String remindBoss(
+        @PathVariable("applicationId") Long applicationId,
+        RedirectAttributes redirectAttributes
+    ) throws UnknownApplicationForLeaveException {
 
         final Application application = applicationService.getApplicationById(applicationId)
             .orElseThrow(() -> new UnknownApplicationForLeaveException(applicationId));
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveViewController.java b/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveViewController.java
index 7cc0ced4f..16690e677 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveViewController.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveViewController.java
@@ -102,7 +102,6 @@ class ApplicationForLeaveViewController implements HasLaunchpad {
         final Person signedInUser = personService.getSignedInUser();
         model.addAttribute("signedInUser", signedInUser);
 
-        model.addAttribute("canAccessApplicationStatistics", isAllowedToAccessApplicationStatistics(signedInUser));
         model.addAttribute("canAccessCancellationRequests", isAllowedToAccessCancellationRequest(signedInUser));
         model.addAttribute("canAccessOtherApplications", isAllowedToAccessOtherApplications(signedInUser));
         model.addAttribute("canAccessSickNoteSubmissions", sickNoteSettings.getUserIsAllowedToSubmitSickNotes() && isAllowedToAccessSickNoteSubmissions(signedInUser));
@@ -154,10 +153,6 @@ class ApplicationForLeaveViewController implements HasLaunchpad {
         }
     }
 
-    private static boolean isAllowedToAccessApplicationStatistics(Person signedInUser) {
-        return signedInUser.hasAnyRole(OFFICE, BOSS, DEPARTMENT_HEAD, SECOND_STAGE_AUTHORITY);
-    }
-
     private static boolean isAllowedToAccessCancellationRequest(Person signedInUser) {
         return signedInUser.hasRole(OFFICE)
             || (signedInUser.hasRole(APPLICATION_CANCELLATION_REQUESTED) && (signedInUser.hasAnyRole(BOSS, DEPARTMENT_HEAD, SECOND_STAGE_AUTHORITY)));
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsService.java b/src/main/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsService.java
index 7990903f4..796b50866 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsService.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsService.java
@@ -37,8 +37,10 @@ class ApplicationForLeaveStatisticsService {
     private final VacationTypeService vacationTypeService;
 
     @Autowired
-    ApplicationForLeaveStatisticsService(PersonService personService, PersonBasedataService personBasedataService, DepartmentService departmentService,
-                                         ApplicationForLeaveStatisticsBuilder applicationForLeaveStatisticsBuilder, VacationTypeService vacationTypeService) {
+    ApplicationForLeaveStatisticsService(
+        PersonService personService, PersonBasedataService personBasedataService, DepartmentService departmentService,
+        ApplicationForLeaveStatisticsBuilder applicationForLeaveStatisticsBuilder, VacationTypeService vacationTypeService
+    ) {
         this.personService = personService;
         this.personBasedataService = personBasedataService;
         this.departmentService = departmentService;
@@ -98,8 +100,12 @@ class ApplicationForLeaveStatisticsService {
             return personService.getActivePersons(query);
         }
 
-        final PageableSearchQuery query = new PageableSearchQuery(sortByPerson ? mapToPersonPageRequest(pageable) : Pageable.unpaged(), pageableSearchQuery.getQuery());
-        return departmentService.getManagedMembersOfPerson(person, query);
+        if (person.isDepartmentPrivileged()) {
+            final PageableSearchQuery query = new PageableSearchQuery(sortByPerson ? mapToPersonPageRequest(pageable) : Pageable.unpaged(), pageableSearchQuery.getQuery());
+            return departmentService.getManagedMembersOfPerson(person, query);
+        }
+
+        return new PageImpl<>(List.of(person), pageable, 1);
     }
 
     private PageRequest mapToPersonPageRequest(Pageable statisticsPageRequest) {
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsViewController.java b/src/main/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsViewController.java
index a3369d9c9..4e98d55d7 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsViewController.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsViewController.java
@@ -15,7 +15,6 @@ import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -50,7 +49,6 @@ import static java.util.stream.Collectors.joining;
 import static org.springframework.http.HttpStatus.OK;
 import static org.springframework.util.StringUtils.hasText;
 import static org.synyx.urlaubsverwaltung.application.statistics.ApplicationForLeaveStatisticsMapper.mapToApplicationForLeaveStatisticsDto;
-import static org.synyx.urlaubsverwaltung.security.SecurityRules.IS_PRIVILEGED_USER;
 import static org.synyx.urlaubsverwaltung.web.html.PaginationPageLinkBuilder.buildPageLinkPrefix;
 
 /**
@@ -73,8 +71,8 @@ class ApplicationForLeaveStatisticsViewController implements HasLaunchpad {
         PersonService personService, ApplicationForLeaveStatisticsService applicationForLeaveStatisticsService,
         ApplicationForLeaveStatisticsCsvExportService applicationForLeaveStatisticsCsvExportService,
         VacationTypeService vacationTypeService, DateFormatAware dateFormatAware, MessageSource messageSource,
-        Clock clock) {
-
+        Clock clock
+    ) {
         this.personService = personService;
         this.applicationForLeaveStatisticsService = applicationForLeaveStatisticsService;
         this.applicationForLeaveStatisticsCsvExportService = applicationForLeaveStatisticsCsvExportService;
@@ -84,7 +82,6 @@ class ApplicationForLeaveStatisticsViewController implements HasLaunchpad {
         this.clock = clock;
     }
 
-    @PreAuthorize(IS_PRIVILEGED_USER)
     @GetMapping
     public String applicationForLeaveStatistics(
         @SortDefault(sort = "person.firstName", direction = Sort.Direction.ASC) Pageable pageable,
@@ -148,7 +145,6 @@ class ApplicationForLeaveStatisticsViewController implements HasLaunchpad {
             .toList();
     }
 
-    @PreAuthorize(IS_PRIVILEGED_USER)
     @GetMapping(value = "/download")
     public ResponseEntity<ByteArrayResource> downloadCSV(
         @SortDefault(sort = "person.firstName", direction = Sort.Direction.ASC) Pageable pageable,
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/person/Person.java b/src/main/java/org/synyx/urlaubsverwaltung/person/Person.java
index 8deb3d04a..b6c6aa7ca 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/person/Person.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/person/Person.java
@@ -20,6 +20,7 @@ import static java.util.Collections.emptyList;
 import static java.util.Collections.unmodifiableCollection;
 import static org.springframework.util.StringUtils.hasText;
 import static org.synyx.urlaubsverwaltung.person.Role.INACTIVE;
+import static org.synyx.urlaubsverwaltung.person.Role.departmentRoles;
 import static org.synyx.urlaubsverwaltung.person.Role.privilegedRoles;
 
 /**
@@ -134,6 +135,11 @@ public class Person extends AbstractTenantAwareEntity {
             .anyMatch(role -> privilegedRoles().contains(role));
     }
 
+    public boolean isDepartmentPrivileged() {
+        return getPermissions().stream()
+            .anyMatch(role -> departmentRoles().contains(role));
+    }
+
     public Collection<MailNotification> getNotifications() {
         if (notifications == null) {
             notifications = emptyList();
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/person/Role.java b/src/main/java/org/synyx/urlaubsverwaltung/person/Role.java
index 859e662f3..f0beba650 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/person/Role.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/person/Role.java
@@ -26,4 +26,8 @@ public enum Role {
     public static List<Role> privilegedRoles() {
         return List.of(DEPARTMENT_HEAD, SECOND_STAGE_AUTHORITY, BOSS, OFFICE);
     }
+
+    public static List<Role> departmentRoles() {
+        return List.of(DEPARTMENT_HEAD, SECOND_STAGE_AUTHORITY);
+    }
 }
diff --git a/src/main/resources/templates/application/application-overview.html b/src/main/resources/templates/application/application-overview.html
index 30690f294..f9400aecc 100644
--- a/src/main/resources/templates/application/application-overview.html
+++ b/src/main/resources/templates/application/application-overview.html
@@ -44,7 +44,6 @@
               href="#"
               th:href="@{/web/application/statistics}"
               class="tw-ml-2 icon-link tw-text-base tw-flex tw-items-center tw-flex-row-reverse tw-space-x-1 sm:tw-space-x-0 sm:tw-flex-row"
-              th:if="${canAccessApplicationStatistics}"
             >
               <svg th:replace="~{icon/presentation-chart-bar::svg(className='tw-w-5 tw-h-5')}"></svg>&nbsp;
               <span th:text="#{action.applications.statistics}"></span>


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveViewControllerTest.java b/src/test/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveViewControllerTest.java
index 4631f10f9..bba0685ad 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveViewControllerTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveViewControllerTest.java
@@ -126,7 +126,6 @@ class ApplicationForLeaveViewControllerTest {
 
         perform(get("/web/application")).andExpect(status().isOk())
             .andExpect(model().attribute("signedInUser", is(userPerson)))
-            .andExpect(model().attribute("canAccessApplicationStatistics", is(false)))
             .andExpect(model().attribute("canAccessCancellationRequests", is(false)))
             .andExpect(model().attribute("canAccessOtherApplications", is(false)))
             .andExpect(model().attribute("canAccessSickNoteSubmissions", is(false)))
@@ -206,7 +205,6 @@ class ApplicationForLeaveViewControllerTest {
 
         perform(get("/web/application")).andExpect(status().isOk())
             .andExpect(model().attribute("signedInUser", is(bossPerson)))
-            .andExpect(model().attribute("canAccessApplicationStatistics", is(true)))
             .andExpect(model().attribute("canAccessCancellationRequests", is(false)))
             .andExpect(model().attribute("canAccessOtherApplications", is(true)))
             .andExpect(model().attribute("canAccessSickNoteSubmissions", is(false)))
@@ -310,7 +308,6 @@ class ApplicationForLeaveViewControllerTest {
 
         perform(get("/web/application")).andExpect(status().isOk())
             .andExpect(model().attribute("signedInUser", is(bossPerson)))
-            .andExpect(model().attribute("canAccessApplicationStatistics", is(true)))
             .andExpect(model().attribute("canAccessCancellationRequests", is(true)))
             .andExpect(model().attribute("canAccessOtherApplications", is(true)))
             .andExpect(model().attribute("canAccessSickNoteSubmissions", is(false)))
@@ -411,7 +408,6 @@ class ApplicationForLeaveViewControllerTest {
 
         perform(get("/web/application")).andExpect(status().isOk())
             .andExpect(model().attribute("signedInUser", is(officePerson)))
-            .andExpect(model().attribute("canAccessApplicationStatistics", is(true)))
             .andExpect(model().attribute("canAccessCancellationRequests", is(true)))
             .andExpect(model().attribute("canAccessOtherApplications", is(true)))
             .andExpect(model().attribute("canAccessSickNoteSubmissions", is(false)))
@@ -512,7 +508,6 @@ class ApplicationForLeaveViewControllerTest {
 
         perform(get("/web/application")).andExpect(status().isOk())
             .andExpect(model().attribute("signedInUser", is(headPerson)))
-            .andExpect(model().attribute("canAccessApplicationStatistics", is(true)))
             .andExpect(model().attribute("canAccessCancellationRequests", is(false)))
             .andExpect(model().attribute("canAccessOtherApplications", is(true)))
             .andExpect(model().attribute("canAccessSickNoteSubmissions", is(false)))
@@ -611,7 +606,6 @@ class ApplicationForLeaveViewControllerTest {
 
         perform(get("/web/application")).andExpect(status().isOk())
             .andExpect(model().attribute("signedInUser", is(headPerson)))
-            .andExpect(model().attribute("canAccessApplicationStatistics", is(true)))
             .andExpect(model().attribute("canAccessCancellationRequests", is(true)))
             .andExpect(model().attribute("canAccessOtherApplications", is(true)))
             .andExpect(model().attribute("canAccessSickNoteSubmissions", is(false)))
@@ -704,7 +698,6 @@ class ApplicationForLeaveViewControllerTest {
 
         perform(get("/web/application")).andExpect(status().isOk())
             .andExpect(model().attribute("signedInUser", is(secondStagePerson)))
-            .andExpect(model().attribute("canAccessApplicationStatistics", is(true)))
             .andExpect(model().attribute("canAccessCancellationRequests", is(false)))
             .andExpect(model().attribute("canAccessOtherApplications", is(true)))
             .andExpect(model().attribute("canAccessSickNoteSubmissions", is(false)))
@@ -814,7 +807,6 @@ class ApplicationForLeaveViewControllerTest {
 
         perform(get("/web/application")).andExpect(status().isOk())
             .andExpect(model().attribute("signedInUser", is(secondStagePerson)))
-            .andExpect(model().attribute("canAccessApplicationStatistics", is(true)))
             .andExpect(model().attribute("canAccessCancellationRequests", is(true)))
             .andExpect(model().attribute("canAccessOtherApplications", is(true)))
             .andExpect(model().attribute("canAccessSickNoteSubmissions", is(false)))
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsServiceTest.java b/src/test/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsServiceTest.java
index 40aa60688..4cdbd18d6 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsServiceTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/application/statistics/ApplicationForLeaveStatisticsServiceTest.java
@@ -78,7 +78,6 @@ class ApplicationForLeaveStatisticsServiceTest {
         when(personService.getActivePersons(personSearchQuery)).thenReturn(new PageImpl<>(List.of(anyPerson)));
 
         final VacationType<?> vacationType = ProvidedVacationType.builder(new StaticMessageSource()).build();
-//        final VacationType<?> vacationType = new VacationType(1L, true, HOLIDAY, "message_key", true, true, YELLOW, false);
         final List<VacationType<?>> activeVacationTypes = List.of(vacationType);
         when(vacationTypeService.getActiveVacationTypes()).thenReturn(activeVacationTypes);
 
@@ -136,8 +135,9 @@ class ApplicationForLeaveStatisticsServiceTest {
         assertThat(applicationForLeaveStatisticsOfPerson.getPersonBasedata()).hasValue(personBasedata);
     }
 
-    @Test
-    void getStatisticsForNotBossOrOffice() {
+    @ParameterizedTest
+    @EnumSource(value = Role.class, names = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY"})
+    void getStatisticsForPersonWithDepartmentPrivileges(Role role) {
 
         final LocalDate startDate = LocalDate.parse("2018-01-01");
         final LocalDate endDate = LocalDate.parse("2018-12-31");
@@ -145,7 +145,7 @@ class ApplicationForLeaveStatisticsServiceTest {
 
         final Person departmentHead = new Person();
         departmentHead.setId(1L);
-        departmentHead.setPermissions(List.of(USER));
+        departmentHead.setPermissions(List.of(USER, role));
 
         final Person departmentMember = new Person();
         departmentMember.setId(2L);
@@ -157,7 +157,6 @@ class ApplicationForLeaveStatisticsServiceTest {
         when(departmentService.getManagedMembersOfPerson(departmentHead, personSearchQuery)).thenReturn(new PageImpl<>(List.of(departmentMember)));
 
         final VacationType<?> vacationType = ProvidedVacationType.builder(new StaticMessageSource()).build();
-//        final VacationType<?> vacationType = new VacationType(1L, true, HOLIDAY, "message_key", true, true, YELLOW, false);
         final List<VacationType<?>> vacationTypes = List.of(vacationType);
         when(vacationTypeService.getActiveVacationTypes()).thenReturn(vacationTypes);
 
@@ -194,7 +193,6 @@ class ApplicationForLeaveStatisticsServiceTest {
         when(personService.getActivePersons(activePersonsPageableSearchQuery)).thenReturn(new PageImpl<>(List.of(anyPerson)));
 
         final VacationType<?> vacationType = ProvidedVacationType.builder(new StaticMessageSource()).build();
-//        final VacationType<?> vacationType = new VacationType(1L, true, HOLIDAY, "message_key", true, true, YELLOW, false);
         final List<VacationType<?>> vacationTypes = List.of(vacationType);
         when(vacationTypeService.getActiveVacationTypes()).thenReturn(vacationTypes);
 
@@ -210,15 +208,17 @@ class ApplicationForLeaveStatisticsServiceTest {
         assertThat(statisticsPage.getContent().get(0).getPerson()).isEqualTo(anyPerson);
     }
 
-    @Test
-    void ensureAllActivePersonsAreRequestedWhenSortedByStatisticsAttributeByNotBossOrOffice() {
+    @ParameterizedTest
+    @EnumSource(value = Role.class, names = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY"})
+    void ensureAllActivePersonsAreRequestedWhenSortedByStatisticsAttributeByDepartmentPrivileged(final Role role) {
 
         final LocalDate startDate = LocalDate.parse("2018-01-01");
         final LocalDate endDate = LocalDate.parse("2018-12-31");
         final FilterPeriod filterPeriod = new FilterPeriod(startDate, endDate);
 
-        final Person notBossOrOfficePerson = new Person();
-        notBossOrOfficePerson.setId(1L);
+        final Person departmentManagement = new Person();
+        departmentManagement.setId(1L);
+        departmentManagement.setPermissions(List.of(USER, role));
 
         final Person departmentMember = new Person();
         departmentMember.setId(2L);
@@ -230,11 +230,10 @@ class ApplicationForLeaveStatisticsServiceTest {
         departmentMemberTwo.setPermissions(List.of(USER));
         departmentMemberTwo.setFirstName("Bernd");
 
-        when(departmentService.getManagedMembersOfPerson(notBossOrOfficePerson, new PageableSearchQuery(Pageable.unpaged(), "")))
+        when(departmentService.getManagedMembersOfPerson(departmentManagement, new PageableSearchQuery(Pageable.unpaged(), "")))
             .thenReturn(new PageImpl<>(List.of(departmentMember, departmentMemberTwo)));
 
         final VacationType<?> vacationType = ProvidedVacationType.builder(new StaticMessageSource()).build();
-//        final VacationType<?> vacationType = new VacationType(1L, true, HOLIDAY, "message_key", true, true, YELLOW, false);
         final List<VacationType<?>> vacationTypes = List.of(vacationType);
         when(vacationTypeService.getActiveVacationTypes()).thenReturn(vacationTypes);
 
@@ -247,10 +246,36 @@ class ApplicationForLeaveStatisticsServiceTest {
         final PageRequest pageRequest = PageRequest.of(0, 10, Sort.Direction.ASC, "person.firstName", "leftVacationDaysForYear");
         final PageableSearchQuery statisticsPageableSearchQuery = new PageableSearchQuery(pageRequest, "");
 
-        final Page<ApplicationForLeaveStatistics> statisticsPage = sut.getStatistics(notBossOrOfficePerson, filterPeriod, statisticsPageableSearchQuery);
+        final Page<ApplicationForLeaveStatistics> statisticsPage = sut.getStatistics(departmentManagement, filterPeriod, statisticsPageableSearchQuery);
 
         assertThat(statisticsPage.getContent()).hasSize(2);
         assertThat(statisticsPage.getContent().get(0).getPerson()).isEqualTo(departmentMember);
         assertThat(statisticsPage.getContent().get(1).getPerson()).isEqualTo(departmentMemberTwo);
     }
+
+    @Test
+    void getStatisticsForUser() {
+
+        final LocalDate startDate = LocalDate.parse("2018-01-01");
+        final LocalDate endDate = LocalDate.parse("2018-12-31");
+        final FilterPeriod filterPeriod = new FilterPeriod(startDate, endDate);
+
+        final Person user = new Person();
+        user.setId(1L);
+        user.setPermissions(List.of(USER));
+
+        final VacationType<?> vacationType = ProvidedVacationType.builder(new StaticMessageSource()).build();
+        final List<VacationType<?>> vacationTypes = List.of(vacationType);
+        when(vacationTypeService.getActiveVacationTypes()).thenReturn(vacationTypes);
+
+        when(applicationForLeaveStatisticsBuilder.build(List.of(user), startDate, endDate, vacationTypes))
+            .thenReturn(Map.of(user, new ApplicationForLeaveStatistics(user, vacationTypes)));
+
+        final PageRequest pageRequest = PageRequest.of(0, 10, Sort.Direction.ASC, "person.firstName");
+        final PageableSearchQuery statisticsPageableSearchQuery = new PageableSearchQuery(pageRequest, "");
+
+        final Page<ApplicationForLeaveStatistics> statisticsPage = sut.getStatistics(user, filterPeriod, statisticsPageableSearchQuery);
+        assertThat(statisticsPage.getContent()).hasSize(1);
+        assertThat(statisticsPage.getContent().get(0).getPerson()).isEqualTo(user);
+    }
 }