diff --git a/src/main/java/de/focusshift/zeiterfassung/overtime/OvertimeDuration.java b/src/main/java/de/focusshift/zeiterfassung/overtime/OvertimeDuration.java new file mode 100644 index 000000000..e8a77be04 --- /dev/null +++ b/src/main/java/de/focusshift/zeiterfassung/overtime/OvertimeDuration.java @@ -0,0 +1,71 @@ +package de.focusshift.zeiterfassung.overtime; + +import de.focusshift.zeiterfassung.timeentry.TimeEntryDuration; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Duration; +import java.util.Objects; + +public final class OvertimeDuration implements TimeEntryDuration { + + public static OvertimeDuration ZERO = new OvertimeDuration(Duration.ZERO); + + private final Duration value; + + public OvertimeDuration(Duration value) { + this.value = value; + } + + @Override + public Duration value() { + return value; + } + + @Override + public Duration minutes() { + final long seconds = value.toSeconds(); + + return seconds % 60 == 0 + ? value + : Duration.ofMinutes(value.toMinutes() + 1); + } + + @Override + public double hoursDoubleValue() { + final long minutes = minutes().toMinutes(); + return minutesToHours(minutes); + } + + public OvertimeDuration plus(Duration duration) { + return new OvertimeDuration(value.plus(duration)); + } + + public OvertimeDuration plus(OvertimeDuration overtimeDuration) { + return new OvertimeDuration(value.plus(overtimeDuration.value)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OvertimeDuration that = (OvertimeDuration) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "OvertimeDuration{" + + "value=" + value + + '}'; + } + + private static double minutesToHours(long minutes) { + return BigDecimal.valueOf(minutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.CEILING).doubleValue(); + } +} diff --git a/src/main/java/de/focusshift/zeiterfassung/report/ReportControllerHelper.java b/src/main/java/de/focusshift/zeiterfassung/report/ReportControllerHelper.java index 5425fff14..fbe3ae15e 100644 --- a/src/main/java/de/focusshift/zeiterfassung/report/ReportControllerHelper.java +++ b/src/main/java/de/focusshift/zeiterfassung/report/ReportControllerHelper.java @@ -1,9 +1,12 @@ package de.focusshift.zeiterfassung.report; +import de.focusshift.zeiterfassung.overtime.OvertimeDuration; +import de.focusshift.zeiterfassung.timeentry.PlannedWorkingHours; import de.focusshift.zeiterfassung.user.DateFormatter; import de.focusshift.zeiterfassung.user.UserId; import de.focusshift.zeiterfassung.usermanagement.User; import de.focusshift.zeiterfassung.usermanagement.UserLocalId; +import org.apache.commons.collections4.SetUtils; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Component; import org.springframework.ui.Model; @@ -14,10 +17,22 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoField; +import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import static java.util.Comparator.comparing; +import static java.util.function.Function.identity; import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; @Component class ReportControllerHelper { @@ -101,6 +116,75 @@ DetailWeekDto toDetailWeekDto(ReportWeek reportWeek, Month monthPivot) { return new DetailWeekDto(Date.from(firstOfWeek.toInstant()), Date.from(lastOfWeek.toInstant()), calendarWeek, dayReports); } + ReportOvertimesDto reportOvertimesDto(ReportWeek reportWeek) { + // person | M | T | W | T | F | S | S | + // ----------------------------------- + // john | 1 | 2 | 2 | 3 | 4 | 4 | 4 | <- `ReportOvertimeDto ( personName, overtimes )` + // jane | 0 | 0 | 2 | 3 | 4 | 4 | 4 | entries in the middle of the week + // jack | 0 | 0 | 0 | 0 | 0 | 0 | 0 | no entries this week + // + // note that the first overtime won't be empty actually, but the `accumulatedOvertimeToDate`. + + // build up `users` peace by peace. one person could have the first working day in the middle of the week (jane). + final Set users = new HashSet<>(); + + // {john} -> [1, 2, 2, 3, 4, 4, 4] + // {jane} -> [empty, empty, 2, 3, 4, 4, 4] + // {jack} -> [empty, empty, empty, empty, empty, empty, empty] (has no entries this week) + final Map>> overtimeDurationsByUser = new HashMap<>(); + + // used to initiate the persons list of overtimes. + // jane will be seen first on the third reportDay. she initially needs a list of `[null, null]`. + int nrOfHandledDays = 0; + + for (ReportDay reportDay : reportWeek.reportDays()) { + + // planned working hours contains all users. even users without time entries at this day + final Map plannedByUser = reportDay.plannedWorkingHoursByUser(); + users.addAll(plannedByUser.keySet()); + + for (User user : users) { + final var durations = overtimeDurationsByUser.computeIfAbsent(user, prepareOvertimeDurationList(nrOfHandledDays)); + durations.add(reportDay.accumulatedOvertimeToDateEndOfBusinessByUser(user.localId())); + } + + nrOfHandledDays++; + } + + final Set userIdsWithDayEntries = users.stream().map(User::localId).collect(toSet()); + final Map> usersWithPlannedWorkingHours = reportWeek.plannedWorkingHoursByUser(); + final Map usersWithPlannedWorkingHoursById = usersWithPlannedWorkingHours.keySet().stream().collect(toMap(User::localId, identity())); + final Set userIdsWithPlannedWorkingHours = usersWithPlannedWorkingHours.keySet().stream().map(User::localId).collect(toSet()); + final SetUtils.SetView userIdsWithoutDayEntries = SetUtils.difference(userIdsWithPlannedWorkingHours, userIdsWithDayEntries); + for (UserLocalId userLocalId : userIdsWithoutDayEntries) { + overtimeDurationsByUser.computeIfAbsent(usersWithPlannedWorkingHoursById.get(userLocalId), prepareOvertimeDurationList(nrOfHandledDays)); + } + + final List overtimeDtos = overtimeDurationsByUser.entrySet().stream() + .map(entry -> new ReportOvertimeDto(entry.getKey().fullName(), overtimeDurationToDouble(entry.getValue()))) + .sorted(comparing(ReportOvertimeDto::personName)) + .collect(toList()); + + return new ReportOvertimesDto(reportWeek.dateOfWeeks(), overtimeDtos); + } + + private static Function>> prepareOvertimeDurationList(int nrOfHandledDays) { + return (unused) -> { + final List> objects = new ArrayList<>(); + for (int i = 0; i < nrOfHandledDays; i++) { + objects.add(Optional.empty()); + } + return objects; + }; + } + + private static List overtimeDurationToDouble(List> overtimeDurations) { + return overtimeDurations.stream() + .map(maybe -> maybe.orElse(null)) + .map(overtimeDuration -> overtimeDuration == null ? null : overtimeDuration.hoursDoubleValue()) + .collect(toList()); + } + String createUrl(String prefix, boolean allUsersSelected, List selectedUserLocalIds) { String url = prefix; diff --git a/src/main/java/de/focusshift/zeiterfassung/report/ReportDay.java b/src/main/java/de/focusshift/zeiterfassung/report/ReportDay.java index 641fb5d69..a8a21fb27 100644 --- a/src/main/java/de/focusshift/zeiterfassung/report/ReportDay.java +++ b/src/main/java/de/focusshift/zeiterfassung/report/ReportDay.java @@ -1,7 +1,9 @@ package de.focusshift.zeiterfassung.report; +import de.focusshift.zeiterfassung.overtime.OvertimeDuration; import de.focusshift.zeiterfassung.timeentry.PlannedWorkingHours; import de.focusshift.zeiterfassung.timeentry.WorkDuration; +import de.focusshift.zeiterfassung.usermanagement.User; import de.focusshift.zeiterfassung.usermanagement.UserLocalId; import java.time.Duration; @@ -10,12 +12,18 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; +import static java.util.function.Function.identity; +import static java.util.function.Predicate.not; +import static java.util.stream.Collectors.toMap; + record ReportDay( LocalDate date, - Map plannedWorkingHoursByUser, + Map plannedWorkingHoursByUser, + Map accumulatedOvertimeToDateByUser, Map> reportDayEntriesByUser ) { @@ -27,26 +35,57 @@ public PlannedWorkingHours plannedWorkingHours() { return plannedWorkingHoursByUser.values().stream().reduce(PlannedWorkingHours.ZERO, PlannedWorkingHours::plus); } - public PlannedWorkingHours plannedWorkingHoursByUser(UserLocalId userLocalId) { - return findValueByFirstKeyMatch(plannedWorkingHoursByUser, userLocalId::equals).orElse(PlannedWorkingHours.ZERO); + public Optional accumulatedOvertimeToDateByUser(UserLocalId userLocalId) { + return findValueByFirstKeyMatch(accumulatedOvertimeToDateByUser, userLocalId::equals); } - public WorkDuration workDuration() { + public Optional accumulatedOvertimeToDateEndOfBusinessByUser(UserLocalId userLocalId) { - final Stream allReportDayEntries = reportDayEntriesByUser.values() + final Optional plannedWorkingHours = plannedWorkingHoursByUser.entrySet() .stream() - .flatMap(Collection::stream); + .filter(entry -> entry.getKey().localId().equals(userLocalId)) + .findFirst() + .map(Map.Entry::getValue); - return calculateWorkDurationFrom(allReportDayEntries); + final Optional overtimeStartOfBusiness = accumulatedOvertimeToDateByUser(userLocalId); + + if (plannedWorkingHours.isEmpty()) { + // TODO how to handle `plannedWorkingHours=null`? it should be `plannedWorkingHours=ZERO` when everything is ok. `null` should only the case for an unknown `userLocalId` i think. + return overtimeStartOfBusiness; + } + + // calculate working time duration of this day + // to add it to `overtimeStartOfBusiness` + + final WorkDuration workDurationThisDay = reportDayEntriesByUser.getOrDefault(userLocalId, List.of()) + .stream() + .filter(not(ReportDayEntry::isBreak)) + .map(ReportDayEntry::workDuration) + .reduce(WorkDuration.ZERO, WorkDuration::plus); + + final Duration overtimeDurationThisDay = plannedWorkingHours.get().value().negated().plus(workDurationThisDay.value()); + final OvertimeDuration overtimeEndOfBusiness = overtimeStartOfBusiness.orElse(OvertimeDuration.ZERO).plus(new OvertimeDuration(overtimeDurationThisDay)); + return Optional.of(overtimeEndOfBusiness); } - public WorkDuration workDurationByUser(UserLocalId userLocalId) { - return workDurationByUserPredicate(userLocalId::equals); + public Map accumulatedOvertimeToDateEndOfBusinessByUser() { + // `accumulatedOvertimeToDateByUser` could not contain persons with timeEntries at this day. + // we need to iterate ALL persons that should have worked this day. + final Map collect = plannedWorkingHoursByUser.keySet() + .stream() + .map(user -> Map.entry(user.localId(), accumulatedOvertimeToDateEndOfBusinessByUser(user.localId()).orElse(OvertimeDuration.ZERO))) + .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); + + return collect; } - private WorkDuration workDurationByUserPredicate(Predicate predicate) { - final List reportDayEntries = findValueByFirstKeyMatch(reportDayEntriesByUser, predicate).orElse(List.of()); - return calculateWorkDurationFrom(reportDayEntries.stream()); + public WorkDuration workDuration() { + + final Stream allReportDayEntries = reportDayEntriesByUser.values() + .stream() + .flatMap(Collection::stream); + + return calculateWorkDurationFrom(allReportDayEntries); } private WorkDuration calculateWorkDurationFrom(Stream reportDayEntries) { @@ -60,9 +99,13 @@ private WorkDuration calculateWorkDurationFrom(Stream reportDayE } private Optional findValueByFirstKeyMatch(Map map, Predicate predicate) { + return findValueByFirstKeyMatch(map, predicate, identity()); + } + + private Optional findValueByFirstKeyMatch(Map map, Predicate predicate, Function keyMapper) { return map.entrySet() .stream() - .filter(entry -> predicate.test(entry.getKey())) + .filter(entry -> predicate.test(keyMapper.apply(entry.getKey()))) .findFirst() .map(Map.Entry::getValue); } diff --git a/src/main/java/de/focusshift/zeiterfassung/report/ReportOvertimeDto.java b/src/main/java/de/focusshift/zeiterfassung/report/ReportOvertimeDto.java new file mode 100644 index 000000000..ac83f41be --- /dev/null +++ b/src/main/java/de/focusshift/zeiterfassung/report/ReportOvertimeDto.java @@ -0,0 +1,10 @@ +package de.focusshift.zeiterfassung.report; + +import java.util.List; + +record ReportOvertimeDto(String personName, List overtimes) { + + public Double overtimeSum() { + return overtimes.stream().reduce(0d, Double::sum); + } +} diff --git a/src/main/java/de/focusshift/zeiterfassung/report/ReportOvertimesDto.java b/src/main/java/de/focusshift/zeiterfassung/report/ReportOvertimesDto.java new file mode 100644 index 000000000..78ca66977 --- /dev/null +++ b/src/main/java/de/focusshift/zeiterfassung/report/ReportOvertimesDto.java @@ -0,0 +1,7 @@ +package de.focusshift.zeiterfassung.report; + +import java.time.LocalDate; +import java.util.List; + +record ReportOvertimesDto(List dayOfWeeks, List overtimes) { +} diff --git a/src/main/java/de/focusshift/zeiterfassung/report/ReportServicePermissionAware.java b/src/main/java/de/focusshift/zeiterfassung/report/ReportServicePermissionAware.java index 4e627c55d..1d2e84d1a 100644 --- a/src/main/java/de/focusshift/zeiterfassung/report/ReportServicePermissionAware.java +++ b/src/main/java/de/focusshift/zeiterfassung/report/ReportServicePermissionAware.java @@ -111,7 +111,7 @@ private ReportWeek emptyReportWeek(Year year, int week) { private ReportWeek emptyReportWeek(LocalDate startOfWeekDate) { final List reportDays = IntStream.rangeClosed(0, 6) - .mapToObj(daysToAdd -> new ReportDay(startOfWeekDate.plusDays(daysToAdd), Map.of(), Map.of())) + .mapToObj(daysToAdd -> new ReportDay(startOfWeekDate.plusDays(daysToAdd), Map.of(), Map.of(), Map.of())) .toList(); return new ReportWeek(startOfWeekDate, reportDays); diff --git a/src/main/java/de/focusshift/zeiterfassung/report/ReportServiceRaw.java b/src/main/java/de/focusshift/zeiterfassung/report/ReportServiceRaw.java index 08b5b64f1..d69352362 100644 --- a/src/main/java/de/focusshift/zeiterfassung/report/ReportServiceRaw.java +++ b/src/main/java/de/focusshift/zeiterfassung/report/ReportServiceRaw.java @@ -1,5 +1,6 @@ package de.focusshift.zeiterfassung.report; +import de.focusshift.zeiterfassung.overtime.OvertimeDuration; import de.focusshift.zeiterfassung.timeentry.PlannedWorkingHours; import de.focusshift.zeiterfassung.timeentry.TimeEntry; import de.focusshift.zeiterfassung.timeentry.TimeEntryService; @@ -19,16 +20,17 @@ import java.time.YearMonth; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.IntStream; import static java.lang.invoke.MethodHandles.lookup; import static java.time.temporal.ChronoUnit.MONTHS; +import static java.util.function.Function.identity; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toMap; @@ -60,22 +62,26 @@ ReportWeek getReportWeek(Year year, int week, UserId userId) { .orElseThrow(() -> new IllegalStateException("could not find user id=%s".formatted(userId))); final UserLocalId userLocalId = user.localId(); + final List users = List.of(user); - return createReportWeek(year, week, + return createReportWeek(year, week, users, period -> Map.of(userLocalId, timeEntryService.getEntries(period.from(), period.toExclusive(), userId)), period -> workingTimeCalendarService.getWorkingTimes(period.from(), period.toExclusive(), List.of(userLocalId))); } ReportWeek getReportWeek(Year year, int week, List userLocalIds) { - return createReportWeek(year, week, - period -> timeEntryService.getEntriesByUserLocalIds(period.from(), period.toExclusive(), userLocalIds), + final List users = userManagementService.findAllUsersByLocalIds(userLocalIds); + return createReportWeek(year, week, users, + period -> timeEntryService.getEntriesByUsers(period.from(), period.toExclusive(), users), period -> workingTimeCalendarService.getWorkingTimes(period.from(), period.toExclusive(), userLocalIds)); } ReportWeek getReportWeekForAllUsers(Year year, int week) { - return createReportWeek(year, week, - period -> timeEntryService.getEntriesForAllUsers(period.from(), period.toExclusive()), - period -> workingTimeCalendarService.getWorkingTimes(period.from(), period.toExclusive())); + final List users = userManagementService.findAllUsers(); + final List userLocalIds = users.stream().map(User::localId).toList(); + return createReportWeek(year, week, users, + period -> timeEntryService.getEntriesByUsers(period.from(), period.toExclusive(), users), + period -> workingTimeCalendarService.getWorkingTimes(period.from(), period.toExclusive(), userLocalIds)); } ReportMonth getReportMonth(YearMonth yearMonth, UserId userId) { @@ -83,94 +89,106 @@ ReportMonth getReportMonth(YearMonth yearMonth, UserId userId) { final User user = userManagementService.findUserById(userId) .orElseThrow(() -> new IllegalStateException("could not find user id=%s".formatted(userId))); - final UserLocalId userLocalId = user.localId(); + final List users = List.of(user); - return createReportMonth(yearMonth, - period -> timeEntryService.getEntriesByUserLocalIds(period.from(), period.toExclusive(), List.of(userLocalId)), + return createReportMonth(yearMonth, users, + period -> timeEntryService.getEntriesByUsers(period.from(), period.toExclusive(), users), period -> workingTimeCalendarService.getWorkingTimes(period.from(), period.toExclusive(), List.of(user.localId()))); } ReportMonth getReportMonth(YearMonth yearMonth, List userLocalIds) { - return createReportMonth(yearMonth, - period -> timeEntryService.getEntriesByUserLocalIds(period.from(), period.toExclusive(), userLocalIds), + final List users = userManagementService.findAllUsersByLocalIds(userLocalIds); + return createReportMonth(yearMonth, users, + period -> timeEntryService.getEntriesByUsers(period.from(), period.toExclusive(), users), period -> workingTimeCalendarService.getWorkingTimes(period.from(), period.toExclusive(), userLocalIds)); } ReportMonth getReportMonthForAllUsers(YearMonth yearMonth) { - return createReportMonth(yearMonth, - period -> timeEntryService.getEntriesForAllUsers(period.from(), period.toExclusive()), - period -> workingTimeCalendarService.getWorkingTimes(period.from(), period.toExclusive())); + final List users = userManagementService.findAllUsers(); + final List userLocalIds = users.stream().map(User::localId).toList(); + return createReportMonth(yearMonth, users, + period -> timeEntryService.getEntriesByUsers(period.from(), period.toExclusive(), users), + period -> workingTimeCalendarService.getWorkingTimes(period.from(), period.toExclusive(), userLocalIds)); } - private ReportWeek createReportWeek(Year year, int week, + private ReportWeek createReportWeek(Year year, int week, List users, Function>> timeEntriesProvider, Function> workingTimeCalendarProvider) { final LocalDate firstDateOfWeek = userDateService.firstDayOfWeek(year, week); - final Period period = new Period(firstDateOfWeek, firstDateOfWeek.plusWeeks(1)); + final Map userById = users.stream().collect(toMap(User::id, identity())); + final Map userByLocalId = users.stream().collect(toMap(User::localId, identity())); + final Map> timeEntries = timeEntriesProvider.apply(period); - final Map userById = userByIdForTimeEntries(timeEntries.values().stream().flatMap(Collection::stream).toList()); final Map workingTimeCalendars = workingTimeCalendarProvider.apply(period); - final Function> plannedWorkingTimeByDate = plannedWorkingTimeForDate(workingTimeCalendars); + final Function> plannedWorkingTimeByDate = plannedWorkingTimeForDate(workingTimeCalendars, userByLocalId); + + // overtime is shown only for the visible week. not the accumulated overtime until this firstDateOfWeek. + // therefore create a map of OvertimeDuration.ZERO + final Map startOfWeekOvertimeByUser = users.stream().collect(toMap(User::localId, (unused) -> OvertimeDuration.ZERO)); - return reportWeek(firstDateOfWeek, timeEntries, userById, plannedWorkingTimeByDate); + return reportWeek(firstDateOfWeek, timeEntries, userById, plannedWorkingTimeByDate, startOfWeekOvertimeByUser); } - private ReportMonth createReportMonth(YearMonth yearMonth, + private ReportMonth createReportMonth(YearMonth yearMonth, List users, Function>> timeEntriesProvider, Function> workingTimeCalendarProvider) { final LocalDate firstOfMonth = LocalDate.of(yearMonth.getYear(), yearMonth.getMonthValue(), 1); - final Period period = new Period(firstOfMonth, firstOfMonth.plusMonths(1)); - final Map> timeEntriesByUserId = timeEntriesProvider.apply(period); - final Map userById = userByIdForTimeEntries(timeEntriesByUserId.values().stream().flatMap(Collection::stream).toList()); + final Map userById = users.stream().collect(toMap(User::id, identity())); + final Map userByLocalId = users.stream().collect(toMap(User::localId, identity())); + final Map> timeEntriesByUserId = timeEntriesProvider.apply(period); final Map workingTimeCalendars = workingTimeCalendarProvider.apply(period); - final Function> plannedWorkingTimeForDate = plannedWorkingTimeForDate(workingTimeCalendars); - - final List weeks = getStartOfWeekDatesForMonth(yearMonth) - .stream() - .map(startOfWeekDate -> - reportWeek( - startOfWeekDate, - timeEntriesByUserId, - userById, - plannedWorkingTimeForDate - ) - ) - .toList(); + final Function> plannedWorkingTimeForDate = plannedWorkingTimeForDate(workingTimeCalendars, userByLocalId); + final Map overtimeStartOfWeekByUser = users.stream().collect(toMap(User::localId, (unused) -> OvertimeDuration.ZERO)); + + final List weeks = new ArrayList<>(); + + for (LocalDate startOfWeek : getStartOfWeekDatesForMonth(yearMonth)) { + final ReportWeek reportWeek = reportWeek( + startOfWeek, + timeEntriesByUserId, + userById, + plannedWorkingTimeForDate, + overtimeStartOfWeekByUser + ); + // add overtime to `overtimeStartOfWeekByUser` for next week + overtimeStartOfWeekByUser.replaceAll(plusOvertimeDuration(reportWeek.overtimeDurationEndOfWeekByUser())); + weeks.add(reportWeek); + } return new ReportMonth(yearMonth, weeks); } - private Function> plannedWorkingTimeForDate(Map workingTimeCalendars) { + private static BiFunction plusOvertimeDuration(Map overtimeDurationEndOfWeekByUser) { + return (userLocalId, overtimeDuration) -> overtimeDurationEndOfWeekByUser.get(userLocalId).plus(overtimeDuration); + } + + private Function> plannedWorkingTimeForDate( + Map workingTimeCalendars, Map userByLocalId) { + return date -> workingTimeCalendars.entrySet() .stream() .collect( toMap( - Map.Entry::getKey, + key -> userByLocalId.get(key.getKey()), entry -> entry.getValue().plannedWorkingHours(date).orElse(PlannedWorkingHours.ZERO) ) ); } - private Map userByIdForTimeEntries(List timeEntries) { - final List userIds = timeEntries.stream().map(TimeEntry::userId).distinct().toList(); - return userManagementService.findAllUsersByIds(userIds) - .stream() - .collect(toMap(User::id, Function.identity())); - } - private ReportWeek reportWeek(LocalDate startOfWeekDate, Map> timeEntriesByUserLocalId, Map userById, - Function> plannedWorkingHoursProvider) { + Function> plannedWorkingHoursProvider, + Map startOfWeekOvertimeByUser) { final Map>> reportEntriesByDate = new HashMap<>(); for (Map.Entry> entry : timeEntriesByUserLocalId.entrySet()) { @@ -196,13 +214,25 @@ private ReportWeek reportWeek(LocalDate startOfWeekDate, final Function>> resolveReportDayEntries = (LocalDate date) -> reportEntriesByDate.getOrDefault(date, Map.of()); + // initial overtime. will be updated in the day iteration below. + final Map overtimeStartOfDayByUser = new HashMap<>(startOfWeekOvertimeByUser); + final List reportDays = IntStream.rangeClosed(0, 6) - .mapToObj(daysToAdd -> - toReportDay( - startOfWeekDate.plusDays(daysToAdd), - plannedWorkingHoursProvider, - resolveReportDayEntries - )) + .mapToObj(daysToAdd -> { + final LocalDate date = startOfWeekDate.plusDays(daysToAdd); + final Map plannedWorkingHoursByUser = plannedWorkingHoursProvider.apply(date); + final Map> dayEntriesByUser = resolveReportDayEntries.apply(date); + final ReportDay day = new ReportDay(date, plannedWorkingHoursByUser, new HashMap<>(overtimeStartOfDayByUser), dayEntriesByUser); + + final Map nextOvertimeStartOfDayByUser = day.accumulatedOvertimeToDateEndOfBusinessByUser(); + + // summarize overtime for existing persons. + overtimeStartOfDayByUser.replaceAll(nextOvertimeStartOfDayByUser::getOrDefault); + // add overtime for new persons + nextOvertimeStartOfDayByUser.forEach(overtimeStartOfDayByUser::putIfAbsent); + + return day; + }) .toList(); return new ReportWeek(startOfWeekDate, reportDays); @@ -238,13 +268,6 @@ private static Optional timeEntryToReportDayEntry(TimeEntry time return Optional.of(first); } - private static ReportDay toReportDay(LocalDate date, - Function> plannedWorkingHoursProvider, - Function>> resolveReportDayEntries) { - - return new ReportDay(date, plannedWorkingHoursProvider.apply(date), resolveReportDayEntries.apply(date)); - } - private static boolean isPreviousMonth(LocalDate possiblePreviousMonthDate, YearMonth yearMonth) { return YearMonth.from(possiblePreviousMonthDate).until(yearMonth, MONTHS) == 1; } diff --git a/src/main/java/de/focusshift/zeiterfassung/report/ReportWeek.java b/src/main/java/de/focusshift/zeiterfassung/report/ReportWeek.java index 696f6624c..d677186e8 100644 --- a/src/main/java/de/focusshift/zeiterfassung/report/ReportWeek.java +++ b/src/main/java/de/focusshift/zeiterfassung/report/ReportWeek.java @@ -1,16 +1,47 @@ package de.focusshift.zeiterfassung.report; +import de.focusshift.zeiterfassung.overtime.OvertimeDuration; import de.focusshift.zeiterfassung.timeentry.PlannedWorkingHours; import de.focusshift.zeiterfassung.timeentry.WorkDuration; +import de.focusshift.zeiterfassung.usermanagement.User; +import de.focusshift.zeiterfassung.usermanagement.UserLocalId; +import java.time.DayOfWeek; import java.time.Duration; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; import static java.util.function.Predicate.not; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.mapping; +import static java.util.stream.Collectors.reducing; record ReportWeek(LocalDate firstDateOfWeek, List reportDays) { + public Map overtimeDurationEndOfWeekByUser() { + return reportDays.stream() + .map(ReportDay::accumulatedOvertimeToDateByUser) + .map(Map::entrySet) + .flatMap(Collection::stream) + .collect(groupingBy( + Map.Entry::getKey, + mapping(Map.Entry::getValue, reducing(OvertimeDuration.ZERO, OvertimeDuration::plus)) + )); + } + + public List dateOfWeeks() { + return IntStream.range(0, 7).mapToObj(d -> firstDateOfWeek().plusDays(d)).toList(); + } + + public List dayOfWeeks() { + return IntStream.range(0, 7).mapToObj(d -> firstDateOfWeek().plusDays(d).getDayOfWeek()).toList(); + } + public PlannedWorkingHours plannedWorkingHours() { return reportDays.stream() .map(ReportDay::plannedWorkingHours) @@ -45,4 +76,23 @@ public WorkDuration workDuration() { public LocalDate lastDateOfWeek() { return firstDateOfWeek.plusDays(6); } + + public Map> plannedWorkingHoursByUser() { + + final Map> plannedWorkingHoursByUserLocalId = new HashMap<>(); + + for (ReportDay reportDay : reportDays) { + reportDay.plannedWorkingHoursByUser().forEach((user, plannedWorkingHours) -> { + plannedWorkingHoursByUserLocalId.compute(user, (unused, planned) -> { + if (planned == null) { + planned = new ArrayList<>(); + } + planned.add(plannedWorkingHours); + return planned; + }); + }); + } + + return plannedWorkingHoursByUserLocalId; + } } diff --git a/src/main/java/de/focusshift/zeiterfassung/report/ReportWeekController.java b/src/main/java/de/focusshift/zeiterfassung/report/ReportWeekController.java index 6332a2557..ada409df4 100644 --- a/src/main/java/de/focusshift/zeiterfassung/report/ReportWeekController.java +++ b/src/main/java/de/focusshift/zeiterfassung/report/ReportWeekController.java @@ -73,15 +73,18 @@ public String weeklyUserReport( final ReportWeek reportWeek = getReportWeek(principal, reportYearWeek, allUsersSelected, reportYear, userLocalIds); final GraphWeekDto graphWeekDto = helper.toGraphWeekDto(reportWeek, reportWeek.firstDateOfWeek().getMonth()); final DetailWeekDto detailWeekDto = helper.toDetailWeekDto(reportWeek, reportWeek.firstDateOfWeek().getMonth()); + final ReportOvertimesDto reportOvertimesDto = helper.reportOvertimesDto(reportWeek); model.addAttribute("weekReport", graphWeekDto); model.addAttribute("weekReportDetail", detailWeekDto); + model.addAttribute("weekReportOvertimes", reportOvertimesDto); final YearWeek todayYearWeek = YearWeek.now(clock); model.addAttribute("isThisWeek", todayYearWeek.equals(reportYearWeek)); model.addAttribute("chartNavigationFragment", "reports/user-report-week::chart-navigation"); model.addAttribute("chartFragment", "reports/user-report-week::chart"); + model.addAttribute("overtimeFragment", "reports/user-overtime-week::data-table"); model.addAttribute("entriesFragment", "reports/user-report-week::entries"); model.addAttribute("weekAriaCurrent", "location"); model.addAttribute("monthAriaCurrent", "false"); diff --git a/src/main/java/de/focusshift/zeiterfassung/timeentry/TimeEntryService.java b/src/main/java/de/focusshift/zeiterfassung/timeentry/TimeEntryService.java index c1faa838f..798dd4b5d 100644 --- a/src/main/java/de/focusshift/zeiterfassung/timeentry/TimeEntryService.java +++ b/src/main/java/de/focusshift/zeiterfassung/timeentry/TimeEntryService.java @@ -1,6 +1,7 @@ package de.focusshift.zeiterfassung.timeentry; import de.focusshift.zeiterfassung.user.UserId; +import de.focusshift.zeiterfassung.usermanagement.User; import de.focusshift.zeiterfassung.usermanagement.UserLocalId; import jakarta.annotation.Nullable; @@ -47,11 +48,11 @@ public interface TimeEntryService { * * @param from first date of interval * @param toExclusive last date (exclusive) of interval - * @param userLocalIds {@linkplain UserLocalId}s of desired users + * @param users desired users * * @return unsorted list of {@linkplain TimeEntry}s grouped by user */ - Map> getEntriesByUserLocalIds(LocalDate from, LocalDate toExclusive, List userLocalIds); + Map> getEntriesByUsers(LocalDate from, LocalDate toExclusive, List users); /** * {@linkplain TimeEntryWeekPage}s for the given user and week of year with sorted {@linkplain TimeEntry}s diff --git a/src/main/java/de/focusshift/zeiterfassung/timeentry/TimeEntryServiceImpl.java b/src/main/java/de/focusshift/zeiterfassung/timeentry/TimeEntryServiceImpl.java index e958ab057..186d79be6 100644 --- a/src/main/java/de/focusshift/zeiterfassung/timeentry/TimeEntryServiceImpl.java +++ b/src/main/java/de/focusshift/zeiterfassung/timeentry/TimeEntryServiceImpl.java @@ -103,13 +103,11 @@ public Map> getEntriesForAllUsers(LocalDate from, L } @Override - public Map> getEntriesByUserLocalIds(LocalDate from, LocalDate toExclusive, List userLocalIds) { + public Map> getEntriesByUsers(LocalDate from, LocalDate toExclusive, List users) { final Instant fromInstant = toInstant(from); final Instant toInstant = toInstant(toExclusive); - final List users = userManagementService.findAllUsersByLocalIds(userLocalIds); - final List userIdValues = users .stream() .map(User::id) @@ -124,6 +122,7 @@ public Map> getEntriesByUserLocalIds(LocalDate from .map(TimeEntryServiceImpl::toTimeEntry) .collect(groupingBy(timeEntry -> userLocalIdById.get(timeEntry.userId()))); + final List userLocalIds = users.stream().map(User::localId).toList(); for (UserLocalId userLocalId : userLocalIds) { result.computeIfAbsent(userLocalId, (unused) -> List.of()); } diff --git a/src/main/java/de/focusshift/zeiterfassung/timeentry/WorkDuration.java b/src/main/java/de/focusshift/zeiterfassung/timeentry/WorkDuration.java index e9a94ec31..127d16abb 100644 --- a/src/main/java/de/focusshift/zeiterfassung/timeentry/WorkDuration.java +++ b/src/main/java/de/focusshift/zeiterfassung/timeentry/WorkDuration.java @@ -35,6 +35,14 @@ public double hoursDoubleValue() { return timeEntryDuration.hoursDoubleValue(); } + public WorkDuration plus(Duration duration) { + return new WorkDuration(value().plus(duration)); + } + + public WorkDuration plus(WorkDuration workDuration) { + return plus(workDuration.value()); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/resources/templates/reports/user-overtime-week.html b/src/main/resources/templates/reports/user-overtime-week.html new file mode 100644 index 000000000..d3a8b079e --- /dev/null +++ b/src/main/resources/templates/reports/user-overtime-week.html @@ -0,0 +1,41 @@ + + + + Zeiterfassung - Bericht + + + + + + + + + + + + + + + + + + + +
+ Überstunden +
+ Person + Überstunden Summe
+ + - +
+
+ + diff --git a/src/main/resources/templates/reports/user-report.html b/src/main/resources/templates/reports/user-report.html index b37f15917..717bf5da8 100644 --- a/src/main/resources/templates/reports/user-report.html +++ b/src/main/resources/templates/reports/user-report.html @@ -55,6 +55,9 @@
+
+
+
diff --git a/src/test/java/de/focusshift/zeiterfassung/report/ReportCsvServiceTest.java b/src/test/java/de/focusshift/zeiterfassung/report/ReportCsvServiceTest.java index 4af293134..08404c556 100644 --- a/src/test/java/de/focusshift/zeiterfassung/report/ReportCsvServiceTest.java +++ b/src/test/java/de/focusshift/zeiterfassung/report/ReportCsvServiceTest.java @@ -88,7 +88,7 @@ void ensureWeekReportCsvRoundsWorkedHoursToTwoDigit() { final ZonedDateTime from = ZonedDateTime.of(LocalDateTime.of(2021, 1, 4, 10, 0), ZONE_ID_BERLIN); final ZonedDateTime to = ZonedDateTime.of(LocalDateTime.of(2021, 1, 4, 10, 30), ZONE_ID_BERLIN); final ReportDayEntry reportDayEntry = new ReportDayEntry(batman, "hard work", from, to, false); - final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman.localId(), PlannedWorkingHours.EIGHT), Map.of(batman.localId(), List.of(reportDayEntry))); + final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman, PlannedWorkingHours.EIGHT), Map.of(), Map.of(batman.localId(), List.of(reportDayEntry))); when(reportService.getReportWeek(Year.of(2021), 1, new UserId("batman"))) .thenReturn(new ReportWeek(LocalDate.of(2020, 12, 28), List.of(reportDay))); @@ -118,13 +118,13 @@ void ensureWeekReportCsvContainsSummarizedInfoPerDay() { final ZonedDateTime d1_2_From = ZonedDateTime.of(LocalDateTime.of(2021, 1, 4, 14, 0), ZONE_ID_BERLIN); final ZonedDateTime d1_2_To = ZonedDateTime.of(LocalDateTime.of(2021, 1, 4, 15, 0), ZONE_ID_BERLIN); final ReportDayEntry d1_2_ReportDayEntry = new ReportDayEntry(batman, "hard work", d1_2_From, d1_2_To, false); - final ReportDay reportDayOne = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman.localId(), PlannedWorkingHours.EIGHT), Map.of(batman.localId(), List.of(d1_1_ReportDayEntry, d1_2_ReportDayEntry))); + final ReportDay reportDayOne = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman, PlannedWorkingHours.EIGHT), Map.of(), Map.of(batman.localId(), List.of(d1_1_ReportDayEntry, d1_2_ReportDayEntry))); // day two final ZonedDateTime d2_1_From = ZonedDateTime.of(LocalDateTime.of(2021, 1, 5, 9, 0), ZONE_ID_BERLIN); final ZonedDateTime d2_1_To = ZonedDateTime.of(LocalDateTime.of(2021, 1, 5, 17, 0), ZONE_ID_BERLIN); final ReportDayEntry d2_1_ReportDayEntry = new ReportDayEntry(batman, "hard work", d2_1_From, d2_1_To, false); - final ReportDay reportDayTwo = new ReportDay(LocalDate.of(2021, 1, 5), Map.of(batman.localId(), PlannedWorkingHours.EIGHT), Map.of(batman.localId(), List.of(d2_1_ReportDayEntry))); + final ReportDay reportDayTwo = new ReportDay(LocalDate.of(2021, 1, 5), Map.of(batman, PlannedWorkingHours.EIGHT), Map.of(), Map.of(batman.localId(), List.of(d2_1_ReportDayEntry))); when(reportService.getReportWeek(Year.of(2021), 1, new UserId("batman"))) @@ -173,7 +173,7 @@ void ensureMonthReportCsvRoundsWorkedHoursToTwoDigit() { final ZonedDateTime from = ZonedDateTime.of(LocalDateTime.of(2021, 1, 4, 10, 0), ZONE_ID_BERLIN); final ZonedDateTime to = ZonedDateTime.of(LocalDateTime.of(2021, 1, 4, 10, 30), ZONE_ID_BERLIN); final ReportDayEntry reportDayEntry = new ReportDayEntry(batman, "hard work", from, to, false); - final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman.localId(), PlannedWorkingHours.EIGHT), Map.of(batman.localId(), List.of(reportDayEntry))); + final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman, PlannedWorkingHours.EIGHT), Map.of(), Map.of(batman.localId(), List.of(reportDayEntry))); final ReportWeek firstWeek = new ReportWeek(LocalDate.of(2020, 12, 28), List.of(reportDay)); final ReportWeek secondWeek = new ReportWeek(LocalDate.of(2021, 1, 4), List.of()); @@ -209,13 +209,13 @@ void ensureMonthReportCsvContainsSummarizedInfoPerDay() { final ZonedDateTime d1_2_From = ZonedDateTime.of(LocalDateTime.of(2021, 1, 4, 14, 0), ZONE_ID_BERLIN); final ZonedDateTime d1_2_To = ZonedDateTime.of(LocalDateTime.of(2021, 1, 4, 15, 0), ZONE_ID_BERLIN); final ReportDayEntry d1_2_ReportDayEntry = new ReportDayEntry(batman, "hard work", d1_2_From, d1_2_To, false); - final ReportDay w1_reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman.localId(), PlannedWorkingHours.EIGHT), Map.of(batman.localId(), List.of(d1_1_ReportDayEntry, d1_2_ReportDayEntry))); + final ReportDay w1_reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman, PlannedWorkingHours.EIGHT), Map.of(), Map.of(batman.localId(), List.of(d1_1_ReportDayEntry, d1_2_ReportDayEntry))); // week two, day one final ZonedDateTime d2_1_From = ZonedDateTime.of(LocalDateTime.of(2021, 1, 5, 9, 0), ZONE_ID_BERLIN); final ZonedDateTime d2_1_To = ZonedDateTime.of(LocalDateTime.of(2021, 1, 5, 17, 0), ZONE_ID_BERLIN); final ReportDayEntry d2_1_ReportDayEntry = new ReportDayEntry(batman, "hard work", d2_1_From, d2_1_To, false); - final ReportDay w2_reportDay = new ReportDay(LocalDate.of(2021, 1, 5), Map.of(batman.localId(), PlannedWorkingHours.EIGHT), Map.of(batman.localId(), List.of(d2_1_ReportDayEntry))); + final ReportDay w2_reportDay = new ReportDay(LocalDate.of(2021, 1, 5), Map.of(batman, PlannedWorkingHours.EIGHT), Map.of(), Map.of(batman.localId(), List.of(d2_1_ReportDayEntry))); final ReportWeek firstWeek = new ReportWeek(LocalDate.of(2020, 12, 28), List.of(w1_reportDay)); final ReportWeek secondWeek = new ReportWeek(LocalDate.of(2021, 1, 4), List.of(w2_reportDay)); diff --git a/src/test/java/de/focusshift/zeiterfassung/report/ReportDayTest.java b/src/test/java/de/focusshift/zeiterfassung/report/ReportDayTest.java index e89d33140..274519ccf 100644 --- a/src/test/java/de/focusshift/zeiterfassung/report/ReportDayTest.java +++ b/src/test/java/de/focusshift/zeiterfassung/report/ReportDayTest.java @@ -31,7 +31,7 @@ void ensureToRemoveBreaks() { final ZonedDateTime to = dateTime(2021, 1, 4, 2, 0); final ReportDayEntry reportDayEntry = new ReportDayEntry(batman, "hard work", from, to, true); - final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman.localId(), PlannedWorkingHours.EIGHT), Map.of(batman.localId(), List.of(reportDayEntry))); + final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batman, PlannedWorkingHours.EIGHT), Map.of(), Map.of(batman.localId(), List.of(reportDayEntry))); assertThat(reportDay.workDuration().value()).isEqualTo(Duration.ZERO); } diff --git a/src/test/java/de/focusshift/zeiterfassung/report/ReportMonthTest.java b/src/test/java/de/focusshift/zeiterfassung/report/ReportMonthTest.java index 3c57fd345..fa77295c3 100644 --- a/src/test/java/de/focusshift/zeiterfassung/report/ReportMonthTest.java +++ b/src/test/java/de/focusshift/zeiterfassung/report/ReportMonthTest.java @@ -57,20 +57,20 @@ private ReportWeek firstWeekFebruary2023(User user, LocalDate firstDateOfWeek, L return new ReportWeek(firstDateOfWeek, List.of( // january - new ReportDay(firstDateOfWeek, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(tuesday, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), + new ReportDay(firstDateOfWeek, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(tuesday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), // february - new ReportDay(wednesday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(wednesday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(wednesday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(wednesday, timeEnd), UTC), false) ))), - new ReportDay(thursday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(thursday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(thursday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(thursday, timeEnd), UTC), false) ))), - new ReportDay(friday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(friday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(friday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(friday, timeEnd), UTC), false) ))), - new ReportDay(saturday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of())), - new ReportDay(sunday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of())) + new ReportDay(saturday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(sunday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of())) )); } @@ -86,23 +86,23 @@ private ReportWeek nthWeekFebruary2023(User user, LocalDate firstDateOfWeek, Loc final UserLocalId userLocalId = user.localId(); return new ReportWeek(firstDateOfWeek, List.of( - new ReportDay(firstDateOfWeek, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(firstDateOfWeek, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(firstDateOfWeek, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(firstDateOfWeek, timeEnd), UTC), false) ))), - new ReportDay(tuesday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(tuesday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(tuesday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(tuesday, timeEnd), UTC), false) ))), - new ReportDay(wednesday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(wednesday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(wednesday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(wednesday, timeEnd), UTC), false) ))), - new ReportDay(thursday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(thursday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(thursday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(thursday, timeEnd), UTC), false) ))), - new ReportDay(friday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(friday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(friday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(friday, timeEnd), UTC), false) ))), - new ReportDay(saturday, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(sunday, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())) + new ReportDay(saturday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(sunday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())) )); } @@ -119,18 +119,18 @@ private ReportWeek lastWeekOfFebruary2023(User user, LocalDate firstDateOfWeek, return new ReportWeek(firstDateOfWeek, List.of( // february - new ReportDay(firstDateOfWeek, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(firstDateOfWeek, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(wednesday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(wednesday, timeEnd), UTC), false) ))), - new ReportDay(tuesday, Map.of(userLocalId, PlannedWorkingHours.EIGHT), Map.of(userLocalId, List.of( + new ReportDay(tuesday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(userLocalId, List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(wednesday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(wednesday, timeEnd), UTC), false) ))), // march - new ReportDay(wednesday, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(thursday, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(friday, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(saturday, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(sunday, Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())) + new ReportDay(wednesday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(thursday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(friday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(saturday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(sunday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())) )); } } diff --git a/src/test/java/de/focusshift/zeiterfassung/report/ReportServicePermissionAwareTest.java b/src/test/java/de/focusshift/zeiterfassung/report/ReportServicePermissionAwareTest.java index 8ef927340..6d3fd1765 100644 --- a/src/test/java/de/focusshift/zeiterfassung/report/ReportServicePermissionAwareTest.java +++ b/src/test/java/de/focusshift/zeiterfassung/report/ReportServicePermissionAwareTest.java @@ -51,13 +51,13 @@ void getReportWeekForMultipleUsersWhenCurrentUserHasNoPermissionForAnyGivenOne() assertThat(actual).isNotNull(); assertThat(actual.firstDateOfWeek()).isEqualTo(LocalDate.of(2023, 2, 13)); assertThat(actual.reportDays()).containsExactly( - new ReportDay(LocalDate.of(2023, 2, 13), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 14), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 15), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 16), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 17), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 18), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 19), Map.of(), Map.of()) + new ReportDay(LocalDate.of(2023, 2, 13), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 14), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 15), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 16), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 17), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 18), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 19), Map.of(), Map.of(), Map.of()) ); } @@ -83,65 +83,65 @@ void getReportMonthForMultipleUsersWhenCurrentUserHasNoPermissionForAnyGivenOne( assertThat(actual.weeks().get(0)).satisfies(week -> { assertThat(week.firstDateOfWeek()).isEqualTo(LocalDate.of(2023, 1, 30)); assertThat(week.reportDays()).containsExactly( - new ReportDay(LocalDate.of(2023, 1, 30), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 1, 31), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 1), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 2), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 3), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 4), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 5), Map.of(), Map.of()) + new ReportDay(LocalDate.of(2023, 1, 30), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 1, 31), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 1), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 2), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 3), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 4), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 5), Map.of(), Map.of(), Map.of()) ); }); assertThat(actual.weeks().get(1)).satisfies(week -> { assertThat(week.firstDateOfWeek()).isEqualTo(LocalDate.of(2023, 2, 6)); assertThat(week.reportDays()).containsExactly( - new ReportDay(LocalDate.of(2023, 2, 6), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 7), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 8), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 9), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 10), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 11), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 12), Map.of(), Map.of()) + new ReportDay(LocalDate.of(2023, 2, 6), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 7), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 8), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 9), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 10), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 11), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 12), Map.of(), Map.of(), Map.of()) ); }); assertThat(actual.weeks().get(2)).satisfies(week -> { assertThat(week.firstDateOfWeek()).isEqualTo(LocalDate.of(2023, 2, 13)); assertThat(week.reportDays()).containsExactly( - new ReportDay(LocalDate.of(2023, 2, 13), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 14), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 15), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 16), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 17), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 18), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 19), Map.of(), Map.of()) + new ReportDay(LocalDate.of(2023, 2, 13), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 14), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 15), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 16), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 17), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 18), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 19), Map.of(), Map.of(), Map.of()) ); }); assertThat(actual.weeks().get(3)).satisfies(week -> { assertThat(week.firstDateOfWeek()).isEqualTo(LocalDate.of(2023, 2, 20)); assertThat(week.reportDays()).containsExactly( - new ReportDay(LocalDate.of(2023, 2, 20), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 21), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 22), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 23), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 24), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 25), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 26), Map.of(), Map.of()) + new ReportDay(LocalDate.of(2023, 2, 20), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 21), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 22), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 23), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 24), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 25), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 26), Map.of(), Map.of(), Map.of()) ); }); assertThat(actual.weeks().get(4)).satisfies(week -> { assertThat(week.firstDateOfWeek()).isEqualTo(LocalDate.of(2023, 2, 27)); assertThat(week.reportDays()).containsExactly( - new ReportDay(LocalDate.of(2023, 2, 27), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 2, 28), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 3, 1), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 3, 2), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 3, 3), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 3, 4), Map.of(), Map.of()), - new ReportDay(LocalDate.of(2023, 3, 5), Map.of(), Map.of()) + new ReportDay(LocalDate.of(2023, 2, 27), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 2, 28), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 3, 1), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 3, 2), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 3, 3), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 3, 4), Map.of(), Map.of(), Map.of()), + new ReportDay(LocalDate.of(2023, 3, 5), Map.of(), Map.of(), Map.of()) ); }); } diff --git a/src/test/java/de/focusshift/zeiterfassung/report/ReportServiceRawTest.java b/src/test/java/de/focusshift/zeiterfassung/report/ReportServiceRawTest.java index ec025dff4..c77f942b7 100644 --- a/src/test/java/de/focusshift/zeiterfassung/report/ReportServiceRawTest.java +++ b/src/test/java/de/focusshift/zeiterfassung/report/ReportServiceRawTest.java @@ -117,8 +117,6 @@ void ensureReportWeekWithOneTimeEntryADay() { when(timeEntryService.getEntries(LocalDate.of(2021, 1, 4), LocalDate.of(2021, 1, 11), userId)) .thenReturn(List.of(firstTimeEntry, secondTimeEntry)); - when(userManagementService.findAllUsersByIds(List.of(userId))).thenReturn(List.of(user)); - final ReportWeek actualReportWeek = sut.getReportWeek(Year.of(2021), 1, userId); assertThat(actualReportWeek.reportDays()).hasSize(7); @@ -153,8 +151,6 @@ void ensureReportWeekWithMultipleTimeEntriesADay() { when(timeEntryService.getEntries(LocalDate.of(2021, 1, 4), LocalDate.of(2021, 1, 11), userId)) .thenReturn(List.of(morningTimeEntry, noonTimeEntry)); - when(userManagementService.findAllUsersByIds(List.of(userId))).thenReturn(List.of(user)); - final ReportWeek actualReportWeek = sut.getReportWeek(Year.of(2021), 1, new UserId("batman")); assertThat(actualReportWeek.reportDays()).hasSize(7); @@ -185,8 +181,6 @@ void ensureReportWeekWithTimeEntryTouchingNextDayIsReportedForStartingDate() { when(timeEntryService.getEntries(LocalDate.of(2021, 1, 4), LocalDate.of(2021, 1, 11), userId)) .thenReturn(List.of(timeEntry)); - when(userManagementService.findAllUsersByIds(List.of(userId))).thenReturn(List.of(user)); - final ReportWeek actualReportWeek = sut.getReportWeek(Year.of(2021), 1, userId); assertThat(actualReportWeek.reportDays()).hasSize(7); @@ -215,7 +209,7 @@ void ensureReportMonthFirstDayOfEveryWeekIsMonday() { when(userDateService.localDateToFirstDateOfWeek(LocalDate.of(2021, 1, 1))) .thenReturn(LocalDate.of(2020, 12, 28)); - when(timeEntryService.getEntriesByUserLocalIds(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1), List.of(localId))) + when(timeEntryService.getEntriesByUsers(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1), List.of(user))) .thenReturn(Map.of()); final ReportMonth actualReportMonth = sut.getReportMonth(YearMonth.of(2021, 1), userId); @@ -238,7 +232,7 @@ void ensureReportMonthDecemberWithoutTimeEntries() { when(userDateService.localDateToFirstDateOfWeek(LocalDate.of(2021, 12, 1))) .thenReturn(LocalDate.of(2021, 11, 29)); - when(timeEntryService.getEntriesByUserLocalIds(LocalDate.of(2021, 12, 1), LocalDate.of(2022, 1, 1), List.of(localId))) + when(timeEntryService.getEntriesByUsers(LocalDate.of(2021, 12, 1), LocalDate.of(2022, 1, 1), List.of(user))) .thenReturn(Map.of()); final ReportMonth actualReportMonth = sut.getReportMonth(YearMonth.of(2021, 12), userId); @@ -295,11 +289,9 @@ void ensureReportMonthDecemberWithOneTimeEntryAWeek() { when(userDateService.localDateToFirstDateOfWeek(LocalDate.of(2021, 1, 1))) .thenReturn(LocalDate.of(2020, 12, 28)); - when(timeEntryService.getEntriesByUserLocalIds(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1), List.of(localId))) + when(timeEntryService.getEntriesByUsers(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1), List.of(user))) .thenReturn(Map.of(localId, List.of(w1_d1_TimeEntry, w1_d2_TimeEntry, w2_d1_TimeEntry, w2_d2_TimeEntry, w3_d1_TimeEntry, w3_d2_TimeEntry, w4_d1_TimeEntry, w4_d2_TimeEntry))); - when(userManagementService.findAllUsersByIds(List.of(userId))).thenReturn(List.of(user)); - final ReportMonth actualReportMonth = sut.getReportMonth(YearMonth.of(2021, 1), userId); assertThat(actualReportMonth.yearMonth()).isEqualTo(YearMonth.of(2021, 1)); diff --git a/src/test/java/de/focusshift/zeiterfassung/report/ReportWeekTest.java b/src/test/java/de/focusshift/zeiterfassung/report/ReportWeekTest.java index 6f37691cf..90d616932 100644 --- a/src/test/java/de/focusshift/zeiterfassung/report/ReportWeekTest.java +++ b/src/test/java/de/focusshift/zeiterfassung/report/ReportWeekTest.java @@ -32,14 +32,15 @@ void ensureAverageDayWorkDurationIsEmptyWhenNoReportDays() { void ensureAverageDayWorkDurationIsEmptyWhenAllReportDaysHasNotPlannedWorkingHours() { final UserLocalId userLocalId = new UserLocalId(1L); + final User user = new User(new UserId("id"), userLocalId, "Bruce", "Wayne", new EMailAddress("bruce@example.org"), Set.of()); final ReportWeek sut = new ReportWeek(LocalDate.of(2023, 2, 13), List.of( - new ReportDay(LocalDate.of(2023, 2, 13), Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(LocalDate.of(2023, 2, 14), Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(LocalDate.of(2023, 2, 15), Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(LocalDate.of(2023, 2, 16), Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(LocalDate.of(2023, 2, 17), Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())), - new ReportDay(LocalDate.of(2023, 2, 18), Map.of(userLocalId, PlannedWorkingHours.ZERO), Map.of(userLocalId, List.of())) + new ReportDay(LocalDate.of(2023, 2, 13), Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(LocalDate.of(2023, 2, 14), Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(LocalDate.of(2023, 2, 15), Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(LocalDate.of(2023, 2, 16), Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(LocalDate.of(2023, 2, 17), Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())), + new ReportDay(LocalDate.of(2023, 2, 18), Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(userLocalId, List.of())) )); assertThat(sut.averageDayWorkDuration()).isEqualTo(WorkDuration.ZERO); @@ -62,23 +63,23 @@ void ensureAverageDayWorkDuration() { final LocalDate sunday = monday.plusDays(6); final ReportWeek sut = new ReportWeek(monday, List.of( - new ReportDay(monday, Map.of(user.localId(), PlannedWorkingHours.EIGHT), Map.of(user.localId(), List.of( + new ReportDay(monday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(user.localId(), List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(monday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(monday, timeEnd), UTC), false) ))), - new ReportDay(tuesday, Map.of(user.localId(), PlannedWorkingHours.EIGHT), Map.of(user.localId(), List.of( + new ReportDay(tuesday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(user.localId(), List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(tuesday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(tuesday, timeEnd), UTC), false) ))), - new ReportDay(wednesday, Map.of(user.localId(), PlannedWorkingHours.EIGHT), Map.of(user.localId(), List.of( + new ReportDay(wednesday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(user.localId(), List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(wednesday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(wednesday, timeEnd), UTC), false) ))), - new ReportDay(thursday, Map.of(user.localId(), PlannedWorkingHours.EIGHT), Map.of(user.localId(), List.of( + new ReportDay(thursday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(user.localId(), List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(thursday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(thursday, timeEnd), UTC), false) ))), - new ReportDay(friday, Map.of(user.localId(), PlannedWorkingHours.EIGHT), Map.of(user.localId(), List.of( + new ReportDay(friday, Map.of(user, PlannedWorkingHours.EIGHT), Map.of(), Map.of(user.localId(), List.of( new ReportDayEntry(user, "", ZonedDateTime.of(LocalDateTime.of(friday, timeStart), UTC), ZonedDateTime.of(LocalDateTime.of(friday, timeEnd), UTC), false) ))), - new ReportDay(saturday, Map.of(user.localId(), PlannedWorkingHours.ZERO), Map.of(user.localId(), List.of())), - new ReportDay(sunday, Map.of(user.localId(), PlannedWorkingHours.ZERO), Map.of(user.localId(), List.of())) + new ReportDay(saturday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(user.localId(), List.of())), + new ReportDay(sunday, Map.of(user, PlannedWorkingHours.ZERO), Map.of(), Map.of(user.localId(), List.of())) )); final WorkDuration actual = sut.averageDayWorkDuration(); diff --git a/src/test/java/de/focusshift/zeiterfassung/timeentry/TimeEntryServiceImplTest.java b/src/test/java/de/focusshift/zeiterfassung/timeentry/TimeEntryServiceImplTest.java index 1c4579fcf..53fd4e84c 100644 --- a/src/test/java/de/focusshift/zeiterfassung/timeentry/TimeEntryServiceImplTest.java +++ b/src/test/java/de/focusshift/zeiterfassung/timeentry/TimeEntryServiceImplTest.java @@ -810,8 +810,6 @@ void ensureGetEntriesByUserLocalIds() { final User batman = new User(new UserId("uuid-1"), batmanLocalId, "Bruce", "Wayne", new EMailAddress("batman@example.org"), Set.of()); final User robin = new User(new UserId("uuid-2"), robinLocalId, "Dick", "Grayson", new EMailAddress("robin@example.org"), Set.of()); - when(userManagementService.findAllUsersByLocalIds(List.of(batmanLocalId, robinLocalId))).thenReturn(List.of(batman, robin)); - final Instant now = Instant.now(); final LocalDate from = LocalDate.of(2023, 1, 1); final LocalDate toExclusive = LocalDate.of(2023, 2, 1); @@ -827,7 +825,7 @@ void ensureGetEntriesByUserLocalIds() { when(timeEntryRepository.findAllByOwnerIsInAndStartGreaterThanEqualAndStartLessThan(List.of("uuid-1", "uuid-2"), from.atStartOfDay(UTC).toInstant(), toExclusive.atStartOfDay(UTC).toInstant())) .thenReturn(List.of(timeEntryEntity, timeEntryBreakEntity)); - final Map> actual = sut.getEntriesByUserLocalIds(from, toExclusive, List.of(batmanLocalId, robinLocalId)); + final Map> actual = sut.getEntriesByUsers(from, toExclusive, List.of(batman, robin)); final ZonedDateTime expectedStart = ZonedDateTime.of(entryStart, ZONE_ID_UTC); final ZonedDateTime expectedEnd = ZonedDateTime.of(entryEnd, ZONE_ID_UTC); @@ -853,16 +851,15 @@ void ensureGetEntriesByUserLocalIds() { void ensureGetEntriesByUserLocalIdsReturnsValuesForEveryAskedUserLocalId() { final UserLocalId batmanLocalId = new UserLocalId(1L); - - when(userManagementService.findAllUsersByLocalIds(List.of(batmanLocalId))).thenReturn(List.of()); + final User batman = new User(new UserId("uuid-1"), batmanLocalId, "Bruce", "Wayne", new EMailAddress("batman@example.org"), Set.of()); final LocalDate from = LocalDate.of(2023, 1, 1); final LocalDate toExclusive = LocalDate.of(2023, 2, 1); - when(timeEntryRepository.findAllByOwnerIsInAndStartGreaterThanEqualAndStartLessThan(List.of(), from.atStartOfDay(UTC).toInstant(), toExclusive.atStartOfDay(UTC).toInstant())) + when(timeEntryRepository.findAllByOwnerIsInAndStartGreaterThanEqualAndStartLessThan(List.of("uuid-1"), from.atStartOfDay(UTC).toInstant(), toExclusive.atStartOfDay(UTC).toInstant())) .thenReturn(List.of()); - final Map> actual = sut.getEntriesByUserLocalIds(from, toExclusive, List.of(batmanLocalId)); + final Map> actual = sut.getEntriesByUsers(from, toExclusive, List.of(batman)); assertThat(actual) .hasSize(1)