Skip to content

Commit

Permalink
Merge pull request #71 from Gepardec/feature/687
Browse files Browse the repository at this point in the history
Feature/687
  • Loading branch information
Ollitod authored Jun 11, 2024
2 parents 8f2ff4c + 37a3d84 commit c8f6358
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.gepardec.mega.service.api.*;
import com.gepardec.mega.service.helper.WorkingTimeUtil;
import com.gepardec.mega.zep.ZepService;
import com.gepardec.mega.zep.impl.Rest;
import io.quarkus.security.Authenticated;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
Expand All @@ -25,6 +26,8 @@
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.YearMonth;
Expand Down Expand Up @@ -66,6 +69,10 @@ public class ManagementResourceImpl implements ManagementResource {
@Inject
EmployeeMapper employeeMapper;

@Inject
@Rest
ZepService zepRestService;

@Inject
Logger logger;

Expand Down Expand Up @@ -303,6 +310,24 @@ private ManagementEntryDto createManagementEntryForEmployee(Employee employee, S
employeeCheckStateReason = employeeCheckStatePair.getRight();
}

// used later on to compute the percentage of hours which were spent in this project (both billable and non-billable)
List<com.gepardec.mega.domain.model.monthlyreport.ProjectEntry> projectEntriesForEmployee = zepRestService.getProjectTimes(employee, from);
long totalWorkingHoursInMinutesForMonthAndEmployee = workingTimeUtil.getDurationFromTimeString(workingTimeUtil.getTotalWorkingTimeForEmployee(projectEntriesForEmployee, employee)).toMinutes();

String billableTimeString = workingTimeUtil.getBillableTimesForEmployee(projectTime, employee);
String nonBillableTimeString = workingTimeUtil.getInternalTimesForEmployee(projectTime, employee);
long billableTimeInMinutes = workingTimeUtil.getDurationFromTimeString(billableTimeString).toMinutes();
long nonBillableTimeInMinutes = workingTimeUtil.getDurationFromTimeString(nonBillableTimeString).toMinutes();

double percentageOfHoursSpentInThisProject = 0.0;
if (!(Double.compare(totalWorkingHoursInMinutesForMonthAndEmployee, 0.0d) == 0)) {
percentageOfHoursSpentInThisProject = (double) (billableTimeInMinutes + nonBillableTimeInMinutes) / totalWorkingHoursInMinutesForMonthAndEmployee;
percentageOfHoursSpentInThisProject =
BigDecimal.valueOf(percentageOfHoursSpentInThisProject)
.setScale(2, RoundingMode.HALF_UP)
.doubleValue() * 100;
}

return ManagementEntryDto.builder()
.employee(employeeMapper.mapToDto(employee))
.employeeCheckState(employeeCheckState)
Expand All @@ -313,8 +338,9 @@ private ManagementEntryDto createManagementEntryForEmployee(Employee employee, S
.finishedComments(finishedAndTotalComments.getFinishedComments())
.totalComments(finishedAndTotalComments.getTotalComments())
.entryDate(stepEntries.get(0).getDate().format(DateTimeFormatter.ofPattern(DATE_FORMAT_PATTERN)))
.billableTime(workingTimeUtil.getBillableTimesForEmployee(projectTime, employee))
.nonBillableTime(workingTimeUtil.getInternalTimesForEmployee(projectTime, employee))
.billableTime(billableTimeString)
.nonBillableTime(nonBillableTimeString)
.percentageOfHoursSpentInThisProject(percentageOfHoursSpentInThisProject)
.build();
}

Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/gepardec/mega/rest/model/ManagementEntryDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class ManagementEntryDto {

private final String nonBillableTime;

private final double percentageOfHoursSpentInThisProject;

private ManagementEntryDto(Builder builder) {
this.employee = builder.employee;
this.employeeCheckState = builder.employeeCheckState;
Expand All @@ -47,6 +49,7 @@ private ManagementEntryDto(Builder builder) {
this.entryDate = builder.entryDate;
this.billableTime = builder.billableTime;
this.nonBillableTime = builder.nonBillableTime;
this.percentageOfHoursSpentInThisProject = builder.percentageOfHoursSpentInThisProject;
}

public static Builder builder() {
Expand Down Expand Up @@ -112,6 +115,10 @@ public String getNonBillableTime() {
return nonBillableTime;
}

public double getPercentageOfHoursSpentInThisProject() {
return percentageOfHoursSpentInThisProject;
}

@JsonPOJOBuilder(withPrefix = "")
public static final class Builder {
private EmployeeDto employee;
Expand All @@ -125,6 +132,7 @@ public static final class Builder {
private String entryDate;
private String billableTime;
private String nonBillableTime;
private double percentageOfHoursSpentInThisProject;

private Builder() {
}
Expand Down Expand Up @@ -188,6 +196,11 @@ public Builder nonBillableTime(String nonBillableTime) {
return this;
}

public Builder percentageOfHoursSpentInThisProject(double percentageOfHoursSpentInThisProject) {
this.percentageOfHoursSpentInThisProject = percentageOfHoursSpentInThisProject;
return this;
}

public ManagementEntryDto build() {
return new ManagementEntryDto(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ public double getOvertimeForEmployee(Employee employee,
return (double) overtime.toMinutes() / 60;
}

public Duration getDurationFromTimeString(String timeString) {
if (timeString == null || timeString.isEmpty()) {
throw new IllegalArgumentException("Time string cannot be null or empty.");
}
if (!timeString.contains(":")) {
throw new IllegalArgumentException(String.format("Invalid time string %s. Expected format is 'HH:MM'.", timeString));
}
String[] parts = timeString.split(":");
return Duration.parse(String.format("PT%sH%sM", parts[0], parts[1]));
}

private static Map.Entry<DayOfWeek, Long> removeAbsenceDays(Map.Entry<DayOfWeek, Long> workingDayEntry,
Map<DayOfWeek, Long> absenceDaysCountMap) {
return Map.entry(
Expand Down Expand Up @@ -155,11 +166,10 @@ private AbsenceTime trimDurationToCurrentMonth(AbsenceTime fehlzeit, LocalDate d
}

return AbsenceTime.builder()
.accepted(fehlzeit.accepted())
.reason(fehlzeit.reason())
.toDate(toDate)
.fromDate(fromDate)
.build();

.accepted(fehlzeit.accepted())
.reason(fehlzeit.reason())
.toDate(toDate)
.fromDate(fromDate)
.build();
}
}
63 changes: 63 additions & 0 deletions src/test/java/com/gepardec/mega/rest/ManagementResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import com.gepardec.mega.db.entity.project.ProjectEntry;
import com.gepardec.mega.db.entity.project.ProjectStep;
import com.gepardec.mega.domain.model.*;
import com.gepardec.mega.domain.model.monthlyreport.ProjectTimeEntry;
import com.gepardec.mega.domain.model.monthlyreport.Task;
import com.gepardec.mega.domain.model.monthlyreport.WorkingLocation;
import com.gepardec.mega.rest.model.ManagementEntryDto;
import com.gepardec.mega.rest.model.ProjectManagementEntryDto;
import com.gepardec.mega.service.api.CommentService;
Expand All @@ -15,6 +18,7 @@
import com.gepardec.mega.service.api.StepEntryService;
import com.gepardec.mega.service.helper.WorkingTimeUtil;
import com.gepardec.mega.zep.ZepService;
import com.gepardec.mega.zep.impl.Rest;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.mockito.InjectMock;
import io.quarkus.test.security.TestSecurity;
Expand All @@ -28,6 +32,7 @@

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

import static io.restassured.RestAssured.given;
Expand Down Expand Up @@ -55,6 +60,7 @@ class ManagementResourceTest {
ProjectEntryService projectEntryService;

@InjectMock
@Rest
ZepService zepService;

@InjectMock
Expand Down Expand Up @@ -312,8 +318,15 @@ void getProjectManagementEntries_whenProjectTimes_thenCorrectAggregatedWorkTimes
ArgumentMatchers.anyString(), any(LocalDate.class)
)).thenReturn(getProjectTimeTypeList());

when(zepService.getProjectTimes(any(Employee.class), any(LocalDate.class)))
.thenReturn(createProjectEntries());

when(workingTimeUtil.getTotalWorkingTimeForEmployee(any(), any(Employee.class))).thenReturn("25:00");
when(workingTimeUtil.getInternalTimesForEmployee(anyList(), any(Employee.class))).thenReturn("01:00");
when(workingTimeUtil.getBillableTimesForEmployee(anyList(), any(Employee.class))).thenReturn("02:00");
when(workingTimeUtil.getDurationFromTimeString(("01:00"))).thenReturn(Duration.ofHours(1));
when(workingTimeUtil.getDurationFromTimeString(("02:00"))).thenReturn(Duration.ofHours(2));
when(workingTimeUtil.getDurationFromTimeString(("25:00"))).thenReturn(Duration.ofHours(25));

List<ProjectManagementEntryDto> result = given().contentType(ContentType.JSON)
.get("/management/projectmanagemententries/2020/09")
Expand Down Expand Up @@ -350,6 +363,7 @@ void getProjectManagementEntries_whenProjectTimes_thenCorrectAggregatedWorkTimes
// assert billable/non billable time
assertThat(result.get(0).getAggregatedBillableWorkTimeInSeconds()).isEqualTo(Duration.ofMinutes(240));
assertThat(result.get(0).getAggregatedNonBillableWorkTimeInSeconds()).isEqualTo(Duration.ofMinutes(120));
assertThat(result.get(0).getEntries().get(0).getPercentageOfHoursSpentInThisProject()).isEqualTo(12);
}

@Test
Expand Down Expand Up @@ -395,6 +409,9 @@ void getProjectManagementEntries_whenNoProjectTimes_thenZeroAggregatedWorkTimes(
ArgumentMatchers.anyString(), any(LocalDate.class)
)).thenReturn(getProjectTimeTypeList());

when(zepService.getProjectTimes(any(Employee.class), any(LocalDate.class)))
.thenReturn(List.of());

when(workingTimeUtil.getBillableTimesForEmployee(anyList(), any(Employee.class))).thenReturn("00:00");
when(workingTimeUtil.getInternalTimesForEmployee(anyList(), any(Employee.class))).thenReturn("00:00");

Expand Down Expand Up @@ -433,6 +450,7 @@ void getProjectManagementEntries_whenNoProjectTimes_thenZeroAggregatedWorkTimes(
// assert billable/non billable time
assertThat(result.get(0).getAggregatedBillableWorkTimeInSeconds()).isEqualTo(Duration.ofMinutes(0));
assertThat(result.get(0).getAggregatedNonBillableWorkTimeInSeconds()).isEqualTo(Duration.ofMinutes(0));
assertThat(result.get(0).getEntries().get(0).getPercentageOfHoursSpentInThisProject()).isEqualTo(0);
}

@Test
Expand Down Expand Up @@ -617,4 +635,49 @@ private List<ProjectTime> getProjectTimeTypeList() {

return timeType;
}

private List<com.gepardec.mega.domain.model.monthlyreport.ProjectEntry> createProjectEntries() {
List<com.gepardec.mega.domain.model.monthlyreport.ProjectEntry> projectEntries = new ArrayList<>();
projectEntries.add(
createEntry(LocalDateTime.of(2024, 5, 21, 14, 30),
LocalDateTime.of(2024, 5, 21, 16, 30))
);

projectEntries.add(
createEntry(LocalDateTime.of(2024, 5, 21, 16, 30),
LocalDateTime.of(2024, 5, 21, 18, 30))
);

projectEntries.add(
createEntry(LocalDateTime.of(2024, 5, 27, 8, 0),
LocalDateTime.of(2024, 5, 27, 14, 0))
);

projectEntries.add(
createEntry(LocalDateTime.of(2024, 5, 28, 8, 0),
LocalDateTime.of(2024, 5, 28, 14, 0))
);

projectEntries.add(
createEntry(LocalDateTime.of(2024, 5, 29, 8, 0),
LocalDateTime.of(2024, 5, 29, 14, 0))
);

projectEntries.add(
createEntry(LocalDateTime.of(2024, 5, 31, 9, 0),
LocalDateTime.of(2024, 5, 31, 12, 0))
);

return projectEntries;
}

private com.gepardec.mega.domain.model.monthlyreport.ProjectEntry createEntry(LocalDateTime from, LocalDateTime to) {
return ProjectTimeEntry.builder()
.fromTime(from)
.toTime(to)
.task(Task.BEARBEITEN)
.workingLocation(WorkingLocation.MAIN)
.process("1033")
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;

import java.time.DayOfWeek;
import java.time.Duration;
Expand All @@ -20,15 +23,27 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;

@QuarkusTest
class WorkingTimeUtilTest {

@Inject
WorkingTimeUtil workingTimeUtil;


static Stream<String> invalidTimeStrings() {
return Stream.of(
"01.30",
"2.00",
"",
null
);
}

@Test
void getInternalTimesForEmployeeTest() {
Employee employee = createEmployee().build();
Expand All @@ -48,7 +63,7 @@ void getBillableTimesForEmployeeTest() {
}

@Test
void getTotalWorkingTimes_ProjectTime(){
void getTotalWorkingTimes_ProjectTime() {
Employee employee = createEmployee().build();
List<ProjectEntry> projectEntries = getProjectentries();
String totalWorkingTimes = workingTimeUtil.getTotalWorkingTimeForEmployee(projectEntries, employee);
Expand Down Expand Up @@ -121,6 +136,28 @@ void getAbsenceTimesForEmployee() {
assertThat(absenceTimesForEmployee).isEqualTo(2);
}

@ParameterizedTest
@CsvSource({
"01:30, PT1H30M",
"00:45, PT0H45M",
"12:00, PT12H0M",
"23:59, PT23H59M",
"00:00, PT0H0M"
})
void getDurationFromTimeString_whenInputStringIsValid_ReturnsDuration(String input, String expected) {
Duration expectedDuration = Duration.parse(expected);
Duration actualDuration = workingTimeUtil.getDurationFromTimeString(input);
assertThat(expectedDuration).isEqualTo(actualDuration);
}


@ParameterizedTest
@MethodSource("invalidTimeStrings")
void getDurationFromTimeString_whenInputStringIsInvalid_ThrowsIllegalArgumentException(String input) {
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> workingTimeUtil.getDurationFromTimeString(input));
}


private List<AbsenceTime> returnFehlzeitTypeList() {
AbsenceTime fehlzeitType = AbsenceTime.builder()
.fromDate(LocalDate.of(2023, 11, 6))
Expand Down

0 comments on commit c8f6358

Please sign in to comment.