Skip to content

Commit

Permalink
WIP: add working hours delta to report view
Browse files Browse the repository at this point in the history
TODO:
- highlight negative delta instead of positive (different to timeentries page)
  - this number is the actual value of interest here for the controlling
    if something is negative, something strange and has to be considered
    if something is positive, then... so what :p
- there are still other hard coded values
  • Loading branch information
bseber committed Oct 9, 2024
1 parent 2d19a9d commit ee19259
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

import java.util.List;

record GraphWeekDto(int calendarWeek, String dateRangeString, List<GraphDayDto> dayReports, Double maxHoursWorked) {
record GraphWeekDto(
int calendarWeek,
String dateRangeString,
List<GraphDayDto> dayReports,
Double maxHoursWorked,
String hoursDelta,
boolean hoursDeltaNegative
) {

public Double graphLegendMaxHour() {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package de.focusshift.zeiterfassung.report;

import de.focusshift.zeiterfassung.absence.Absence;
import de.focusshift.zeiterfassung.timeentry.ShouldWorkingHours;
import de.focusshift.zeiterfassung.timeentry.WorkDuration;
import de.focusshift.zeiterfassung.user.DateFormatter;
import de.focusshift.zeiterfassung.user.DateRangeFormatter;
import de.focusshift.zeiterfassung.user.UserId;
Expand Down Expand Up @@ -76,7 +78,19 @@ GraphWeekDto toGraphWeekDto(ReportWeek reportWeek, Month monthPivot) {
.mapToDouble(value -> value)
.max().orElse(0.0);

return new GraphWeekDto(calendarWeek, dateRangeString, dayReports, maxHoursWorked);
final WorkDuration workDuration = reportWeek.workDuration();
final ShouldWorkingHours shouldWorkingHours = reportWeek.shouldWorkingHours();

final Duration deltaDuration = workDuration.duration().minus(shouldWorkingHours.duration());
final String deltaHours = durationToTimeString(deltaDuration);

return new GraphWeekDto(calendarWeek, dateRangeString, dayReports, maxHoursWorked, deltaHours, deltaDuration.isNegative());
}

private static String durationToTimeString(Duration duration) {
// use positive values to format duration string
// negative value is handled in template
return String.format("%02d:%02d", Math.abs(duration.toHours()), Math.abs(duration.toMinutesPart()));
}

private GraphDayDto toUserReportDayReportDto(ReportDay reportDay, boolean differentMonth) {
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/de/focusshift/zeiterfassung/report/ReportDay.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package de.focusshift.zeiterfassung.report;

import de.focusshift.zeiterfassung.absence.Absence;
import de.focusshift.zeiterfassung.absence.DayLength;
import de.focusshift.zeiterfassung.timeentry.ShouldWorkingHours;
import de.focusshift.zeiterfassung.timeentry.WorkDuration;
import de.focusshift.zeiterfassung.user.UserIdComposite;
import de.focusshift.zeiterfassung.usermanagement.UserLocalId;
Expand Down Expand Up @@ -29,6 +32,28 @@ public PlannedWorkingHours plannedWorkingHours() {
return plannedWorkingHoursByUser.values().stream().reduce(PlannedWorkingHours.ZERO, PlannedWorkingHours::plus);
}

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));
}

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

public PlannedWorkingHours plannedWorkingHoursByUser(UserLocalId userLocalId) {
return findValueByFirstKeyMatch(plannedWorkingHoursByUser, userIdComposite -> userLocalId.equals(userIdComposite.localId()))
.orElse(PlannedWorkingHours.ZERO);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.focusshift.zeiterfassung.report;

import de.focusshift.zeiterfassung.timeentry.ShouldWorkingHours;
import de.focusshift.zeiterfassung.timeentry.WorkDuration;
import de.focusshift.zeiterfassung.workingtime.PlannedWorkingHours;

Expand All @@ -17,6 +18,12 @@ public PlannedWorkingHours plannedWorkingHours() {
.reduce(PlannedWorkingHours.ZERO, PlannedWorkingHours::plus);
}

public ShouldWorkingHours shouldWorkingHours() {
return reportDays.stream()
.map(ReportDay::shouldWorkingHours)
.reduce(ShouldWorkingHours.ZERO, ShouldWorkingHours::plus);
}

public WorkDuration averageDayWorkDuration() {

final double averageMinutes = reportDays().stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package de.focusshift.zeiterfassung.timeentry;

import de.focusshift.zeiterfassung.absence.Absence;
import de.focusshift.zeiterfassung.workingtime.PlannedWorkingHours;
import de.focusshift.zeiterfassung.workingtime.ZeitDuration;

import java.time.Duration;

/**
* Hours that should be worked. (e.g. PlannedWorkingHours 40h - Absence 8h = ShouldWorkingHours 32h)
* Hours that should be worked.
*
* <p>
* (e.g. {@linkplain PlannedWorkingHours} 40h - {@linkplain Absence} 8h = ShouldWorkingHours 32h)
*
* @param duration the exact duration. not rounded up to minutes.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ private static ShouldWorkingHours dayShouldHoursWorked(PlannedWorkingHours plann
}

return new ShouldWorkingHours(plannedWorkingHours.duration());

}

private void updateEntityTimeSpan(TimeEntryEntity entity, ZonedDateTime start, ZonedDateTime end, Duration duration)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package de.focusshift.zeiterfassung.workingtime;

import de.focusshift.zeiterfassung.absence.Absence;
import de.focusshift.zeiterfassung.timeentry.ShouldWorkingHours;

import java.time.Duration;

/**
* Defines a {@linkplain Duration} of planned working hours.
* Defines a {@linkplain Duration} of planned working hours. This does not include {@linkplain Absence}s.
* e.g. the employment contract of 40h a week.
*
* <p>
* see {@linkplain ShouldWorkingHours} if you are interested in a value that includes sick days for instance.
*
* @param duration the exact duration. not rounded up to minutes.
*/
Expand Down
35 changes: 22 additions & 13 deletions src/main/resources/templates/reports/user-report-week.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,28 @@
<p
class="tabular-nums text-sm text-gray-800 font-medium sm:flex-1 sm:absolute sm:left-0 sm:right-0 sm:text-center"
>
<span class="inline-flex items-center">+01:58</span>
<!-- <span class="inline-flex items-center">-->
<!-- <th:block th:if="${week.hoursDeltaNegative}">-</th:block>-->
<!-- <th:block th:if="${not week.hoursDeltaNegative}">+</th:block>-->
<!-- <span class="font-bold tabular-nums" th:text="${week.hoursDelta}">-->
<!-- 01:30-->
<!-- </span>-->
<!-- <th:block th:if="${not week.hoursDeltaNegative and week.hoursDelta != '00:00'}">-->
<!-- <svg-->
<!-- th:replace="~{icons/trending-up::svg(className='w-4 h-4 ml-1')}"-->
<!-- ></svg>-->
<!-- </th:block>-->
<!-- </span>-->
<!-- <span class="inline-flex items-center">+01:58</span>-->
<span class="inline-flex items-center">
<th:block th:if="${weekReport.hoursDeltaNegative}"
>-</th:block
>
<th:block th:if="${not weekReport.hoursDeltaNegative}"
>+</th:block
>
<span
class="font-bold tabular-nums"
th:text="${weekReport.hoursDelta}"
>
01:30
</span>
<th:block
th:if="${not weekReport.hoursDeltaNegative and weekReport.hoursDelta != '00:00'}"
>
<svg
th:replace="~{icons/trending-up::svg(className='w-4 h-4 ml-1')}"
></svg>
</th:block>
</span>
</p>
<p class="sr-only sm:not-sr-only">
Geleistet:
Expand Down

0 comments on commit ee19259

Please sign in to comment.