Skip to content

Commit

Permalink
Make shouldWorkingHours implementation aware of multiple users
Browse files Browse the repository at this point in the history
fixes #905
  • Loading branch information
honnel authored and derTobsch committed Oct 31, 2024
1 parent eec9f4b commit 088715b
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 18 deletions.
46 changes: 30 additions & 16 deletions src/main/java/de/focusshift/zeiterfassung/report/ReportDay.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
import de.focusshift.zeiterfassung.usermanagement.UserLocalId;
import de.focusshift.zeiterfassung.workingtime.PlannedWorkingHours;

import java.time.Duration;
import java.time.LocalDate;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toMap;

record ReportDay(
LocalDate date,
Map<UserIdComposite, PlannedWorkingHours> plannedWorkingHoursByUser,
Expand All @@ -33,24 +37,34 @@ public PlannedWorkingHours plannedWorkingHours() {

public ShouldWorkingHours shouldWorkingHours() {

final double absenceDayLengthValue = detailDayAbsencesByUser.values().stream()
.flatMap(Collection::stream)
.map(ReportDayAbsence::absence)
.map(Absence::dayLength)
.map(DayLength::getValue)
.reduce(0.0, Double::sum);

if (absenceDayLengthValue >= 1.0) {
return ShouldWorkingHours.ZERO;
}

final PlannedWorkingHours plannedWorkingHours = plannedWorkingHours();

if (absenceDayLengthValue == 0.5) {
return new ShouldWorkingHours(plannedWorkingHours.duration().dividedBy(2));
final Map<UserIdComposite, Double> absenceLengthByUser = detailDayAbsencesByUser.entrySet().stream()
.map(entry ->
new AbstractMap.SimpleEntry<>(
entry.getKey(),
entry.getValue().stream().map(ReportDayAbsence::absence)
.map(Absence::dayLength)
.map(DayLength::getValue)
.reduce(0.0, Double::sum)
)
)
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));

Duration plannedWorkingHoursOfAllUsers = plannedWorkingHours().duration();

for (final Map.Entry<UserIdComposite, Double> absenceLengths : absenceLengthByUser.entrySet()) {

final Duration plannedWorkingHoursOfUser = plannedWorkingHoursByUser.get(absenceLengths.getKey()).duration();
final Double absenceLength = absenceLengths.getValue();

if (absenceLength == 1.0) {
plannedWorkingHoursOfAllUsers = plannedWorkingHoursOfAllUsers.minus(plannedWorkingHoursOfUser);
}
else if (absenceLength == 0.5) {
plannedWorkingHoursOfAllUsers = plannedWorkingHoursOfAllUsers.minus(plannedWorkingHoursOfUser.dividedBy(2));
}
}

return new ShouldWorkingHours(plannedWorkingHours.duration());
return new ShouldWorkingHours(plannedWorkingHoursOfAllUsers);
}

public PlannedWorkingHours plannedWorkingHoursByUser(UserLocalId userLocalId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package de.focusshift.zeiterfassung.report;

import de.focusshift.zeiterfassung.absence.Absence;
import de.focusshift.zeiterfassung.absence.DayLength;
import de.focusshift.zeiterfassung.tenancy.user.EMailAddress;
import de.focusshift.zeiterfassung.timeentry.ShouldWorkingHours;
import de.focusshift.zeiterfassung.user.UserId;
import de.focusshift.zeiterfassung.user.UserIdComposite;
import de.focusshift.zeiterfassung.usermanagement.User;
import de.focusshift.zeiterfassung.usermanagement.UserLocalId;
import de.focusshift.zeiterfassung.workingtime.PlannedWorkingHours;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

import java.time.Duration;
import java.time.LocalDate;
Expand All @@ -17,12 +22,19 @@
import java.util.Map;
import java.util.Set;

import static de.focusshift.zeiterfassung.absence.AbsenceColor.RED;
import static de.focusshift.zeiterfassung.absence.AbsenceTypeCategory.HOLIDAY;
import static de.focusshift.zeiterfassung.absence.DayLength.FULL;
import static org.assertj.core.api.Assertions.assertThat;

class ReportDayTest {

private static final ZoneId ZONE_ID_BERLIN = ZoneId.of("Europe/Berlin");

private static ZonedDateTime dateTime(int year, int month, int dayOfMonth, int hour, int minute) {
return ZonedDateTime.of(LocalDateTime.of(year, month, dayOfMonth, hour, minute), ZONE_ID_BERLIN);
}

@Test
void ensureToRemoveBreaks() {

Expand All @@ -40,7 +52,87 @@ void ensureToRemoveBreaks() {
assertThat(reportDay.workDuration().duration()).isEqualTo(Duration.ZERO);
}

private static ZonedDateTime dateTime(int year, int month, int dayOfMonth, int hour, int minute) {
return ZonedDateTime.of(LocalDateTime.of(year, month, dayOfMonth, hour, minute), ZONE_ID_BERLIN);
@Test
void shouldWorkingHoursReturnsZeroForFullyAbsentUser() {

final UserId batmanId = new UserId("uuid");
final UserLocalId batmanLocalId = new UserLocalId(1337L);
final UserIdComposite batmanIdComposite = new UserIdComposite(batmanId, batmanLocalId);
final User batman = new User(batmanIdComposite, "Bruce", "Wayne", new EMailAddress("[email protected]"), Set.of());

final ZonedDateTime from = dateTime(2021, 1, 4, 1, 0);
final ZonedDateTime to = dateTime(2021, 1, 4, 2, 0);
Absence absence = new Absence(batmanId, from, to, FULL, locale -> "foo", RED, HOLIDAY);

final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batmanIdComposite, PlannedWorkingHours.EIGHT), Map.of(batmanIdComposite, List.of()), Map.of(batmanIdComposite, List.of(new ReportDayAbsence(batman, absence))));

assertThat(reportDay.shouldWorkingHours()).isEqualTo(new ShouldWorkingHours(Duration.ZERO));
}

@ParameterizedTest
@EnumSource(value = DayLength.class, names = {"MORNING", "NOON"})
void shouldWorkingHoursReturnsHalfShouldWorkingHoursForHalfDayAbsentUser(DayLength dayLength) {

final UserId batmanId = new UserId("uuid");
final UserLocalId batmanLocalId = new UserLocalId(1337L);
final UserIdComposite batmanIdComposite = new UserIdComposite(batmanId, batmanLocalId);
final User batman = new User(batmanIdComposite, "Bruce", "Wayne", new EMailAddress("[email protected]"), Set.of());

final ZonedDateTime from = dateTime(2021, 1, 4, 1, 0);
final ZonedDateTime to = dateTime(2021, 1, 4, 2, 0);
Absence absence = new Absence(batmanId, from, to, dayLength, locale -> "foo", RED, HOLIDAY);

final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), Map.of(batmanIdComposite, PlannedWorkingHours.EIGHT), Map.of(batmanIdComposite, List.of()), Map.of(batmanIdComposite, List.of(new ReportDayAbsence(batman, absence))));

assertThat(reportDay.shouldWorkingHours()).isEqualTo(new ShouldWorkingHours(Duration.ofHours(4)));
}

@Test
void shouldWorkingHoursReturnsShouldHoursForOneAbsentUserAndOneWorkingUser() {

final UserId batmanId = new UserId("uuid1");
final UserLocalId batmanLocalId = new UserLocalId(1337L);
final UserIdComposite batmanIdComposite = new UserIdComposite(batmanId, batmanLocalId);
final User batman = new User(batmanIdComposite, "Bruce", "Wayne", new EMailAddress("[email protected]"), Set.of());

final ZonedDateTime from = dateTime(2021, 1, 4, 1, 0);
final ZonedDateTime to = dateTime(2021, 1, 4, 2, 0);
Absence absence = new Absence(batmanId, from, to, FULL, locale -> "foo", RED, HOLIDAY);

final UserId robinId = new UserId("uuid2");
final UserLocalId robinLocalId = new UserLocalId(1337L);
final UserIdComposite robinIdComposite = new UserIdComposite(robinId, robinLocalId);

Map<UserIdComposite, List<ReportDayEntry>> entriesByUser = Map.of(batmanIdComposite, List.of(), robinIdComposite, List.of());
Map<UserIdComposite, List<ReportDayAbsence>> absencesByUser = Map.of(batmanIdComposite, List.of(new ReportDayAbsence(batman, absence)), robinIdComposite, List.of());
Map<UserIdComposite, PlannedWorkingHours> plannedWorkingHoursByUser = Map.of(batmanIdComposite, PlannedWorkingHours.EIGHT, robinIdComposite, new PlannedWorkingHours(Duration.ofHours(4L)));
final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), plannedWorkingHoursByUser, entriesByUser, absencesByUser);

assertThat(reportDay.shouldWorkingHours()).isEqualTo(new ShouldWorkingHours(Duration.ofHours(4L)));
}

@ParameterizedTest
@EnumSource(value = DayLength.class, names = {"MORNING", "NOON"})
void shouldWorkingHoursReturnsSummedShouldHoursForOneHalfAbsentUserAndOneWorkingUser(DayLength dayLength) {

final UserId batmanId = new UserId("uuid1");
final UserLocalId batmanLocalId = new UserLocalId(1337L);
final UserIdComposite batmanIdComposite = new UserIdComposite(batmanId, batmanLocalId);
final User batman = new User(batmanIdComposite, "Bruce", "Wayne", new EMailAddress("[email protected]"), Set.of());

final ZonedDateTime from = dateTime(2021, 1, 4, 1, 0);
final ZonedDateTime to = dateTime(2021, 1, 4, 2, 0);
Absence absence = new Absence(batmanId, from, to, dayLength, locale -> "foo", RED, HOLIDAY);

final UserId robinId = new UserId("uuid2");
final UserLocalId robinLocalId = new UserLocalId(1337L);
final UserIdComposite robinIdComposite = new UserIdComposite(robinId, robinLocalId);

Map<UserIdComposite, List<ReportDayEntry>> entriesByUser = Map.of(batmanIdComposite, List.of(), robinIdComposite, List.of());
Map<UserIdComposite, List<ReportDayAbsence>> absencesByUser = Map.of(batmanIdComposite, List.of(new ReportDayAbsence(batman, absence)), robinIdComposite, List.of());
Map<UserIdComposite, PlannedWorkingHours> plannedWorkingHoursByUser = Map.of(batmanIdComposite, PlannedWorkingHours.EIGHT, robinIdComposite, new PlannedWorkingHours(Duration.ofHours(4L)));
final ReportDay reportDay = new ReportDay(LocalDate.of(2021, 1, 4), plannedWorkingHoursByUser, entriesByUser, absencesByUser);

assertThat(reportDay.shouldWorkingHours()).isEqualTo(new ShouldWorkingHours(Duration.ofHours(8L)));
}
}

0 comments on commit 088715b

Please sign in to comment.