Skip to content

Commit

Permalink
#405 include workingdaytype in matrix
Browse files Browse the repository at this point in the history
  • Loading branch information
KlausRicharz committed Aug 19, 2024
1 parent 8bf73ab commit d0e8d7e
Show file tree
Hide file tree
Showing 16 changed files with 164 additions and 51 deletions.
8 changes: 8 additions & 0 deletions src/main/java/org/tb/common/BusinessRuleChecks.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.tb.common;

import java.util.Collection;
import lombok.experimental.UtilityClass;
import org.tb.common.exception.BusinessRuleException;

Expand All @@ -17,4 +18,11 @@ public static void notEmpty(String value, ErrorCode errorCode) {
throw new BusinessRuleException(errorCode);
}
}

public static void empty(Collection<?> collection, ErrorCode errorCode) {
if (collection != null && !collection.isEmpty()) {
throw new BusinessRuleException(errorCode);
}
}

}
2 changes: 2 additions & 0 deletions src/main/java/org/tb/common/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public enum ErrorCode {
TR_YEAR_OUT_OF_RANGE("TR-0022","Time reports must be modified only in the current, the previous or the next year"),
TR_DURATION_OVERTIME_COMPENSATION_INVALID("TR-0023","Overtime compensations must always be booked with 0 time"),
TR_WORKING_DAY_START_NULL("TR-0024","the start of the working day must not be null"),
TR_WORKING_DAY_NOT_WORKED("TR-0025","the working day must not be 'not worked'"),
WD_NOT_WORKED_TIMEREPORTS_FOUND("WD-0001","time reports found, please move or delete first!"),
;

private final String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ protected Workingday refreshWorkingday(ShowDailyReportForm reportForm, HttpServl
if (workingday != null) {

//show break time, quitting time and working day ends on the showdailyreport.jsp
request.getSession().setAttribute("visibleworkingday", true);
request.getSession().setAttribute("visibleworkingday", workingday.getType() != WorkingDayType.NOT_WORKED);

reportForm.setSelectedWorkHourBegin(workingday.getStarttimehour());
reportForm.setSelectedWorkMinuteBegin(workingday.getStarttimeminute());
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/org/tb/dailyreport/action/ShowDailyReportAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import org.tb.dailyreport.persistence.TimereportDAO;
import org.tb.dailyreport.persistence.WorkingdayDAO;
import org.tb.dailyreport.service.TimereportService;
import org.tb.dailyreport.service.WorkingdayService;
import org.tb.dailyreport.viewhelper.TimereportHelper;
import org.tb.employee.domain.Employee;
import org.tb.employee.domain.Employeecontract;
Expand Down Expand Up @@ -100,6 +101,7 @@ public class ShowDailyReportAction extends DailyReportAction<ShowDailyReportForm
private final EmployeeorderDAO employeeorderDAO;
private final FavoriteService favoriteService;
private final WorkingdayDAO workingdayDAO;
private final WorkingdayService workingdayService;
private final EmployeeDAO employeeDAO;
private final SuborderHelper suborderHelper;
private final CustomerorderHelper customerorderHelper;
Expand Down Expand Up @@ -726,9 +728,15 @@ private ActionForward doSaveWorkingDay(ActionMapping mapping, HttpServletRequest
// unreachable code
assert false;
}
workingdayDAO.save(workingday);
try {
workingdayService.upsertWorkingday(workingday);
} catch(BusinessRuleException e) {
addToErrors(request, e.getErrorCode());
return mapping.getInputForward();
}

//show break time, quitting time and working day ends on the showdailyreport.jsp
request.getSession().setAttribute("visibleworkingday", true);
request.getSession().setAttribute("visibleworkingday", workingday.getType() != WorkingDayType.NOT_WORKED);
request.getSession().setAttribute("quittingtime", timereportHelper.calculateQuittingTime(workingday, request, "quittingtime"));
//calculate Working Day End
request.getSession().setAttribute("workingDayEnds", timereportHelper.calculateQuittingTime(workingday, request, "workingDayEnds"));
Expand Down Expand Up @@ -875,7 +883,7 @@ private String init(HttpServletRequest request, ShowDailyReportForm reportForm)
if (workingday != null) {
// show break time, quitting time and working day ends on
// the showdailyreport.jsp
request.getSession().setAttribute("visibleworkingday", true);
request.getSession().setAttribute("visibleworkingday", workingday.getType() != WorkingDayType.NOT_WORKED);
reportForm.setSelectedWorkHourBegin(workingday.getStarttimehour());
reportForm.setSelectedWorkMinuteBegin(workingday.getStarttimeminute());
reportForm.setSelectedBreakHour(workingday.getBreakhours());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ public ActionForward executeAuthenticated(ActionMapping mapping, AddDailyReportF
if (workingday != null) {
// show break time, quitting time and working day ends on the
// showdailyreport.jsp
request.getSession().setAttribute("visibleworkingday", true);
request.getSession().setAttribute("visibleworkingday", workingday.getType() != WorkingDayType.NOT_WORKED);
continueForm.setSelectedWorkHourBegin(workingday.getStarttimehour());
continueForm.setSelectedWorkMinuteBegin(workingday.getStarttimeminute());
continueForm.setSelectedBreakHour(workingday.getBreakhours());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public ActionForward executeAuthenticated(ActionMapping mapping, UpdateDailyRepo
if (workingday != null) {

//show break time, quitting time and working day ends on the showdailyreport.jsp
request.getSession().setAttribute("visibleworkingday", true);
request.getSession().setAttribute("visibleworkingday", workingday.getType() != WorkingDayType.NOT_WORKED);

showDailyReportForm.setSelectedWorkHourBegin(workingday.getStarttimehour());
showDailyReportForm.setSelectedWorkMinuteBegin(workingday.getStarttimeminute());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.tb.common.util.DateUtils;
import org.tb.dailyreport.domain.Workingday;
import org.tb.dailyreport.persistence.WorkingdayDAO;
import org.tb.dailyreport.service.WorkingdayService;
import org.tb.employee.persistence.EmployeecontractDAO;

import java.time.LocalDate;
Expand All @@ -25,6 +26,7 @@ public class WorkingDayRestEndpoint {

private final EmployeecontractDAO employeecontractDAO;
private final WorkingdayDAO workingdayDAO;
private WorkingdayService workingdayService;
private final AuthorizedUser authorizedUser;

@PutMapping(consumes = APPLICATION_JSON_VALUE)
Expand All @@ -51,7 +53,10 @@ public void upsert(@RequestBody WorkingDayData data) {
if(data.getType() != null) {
wd.setType(data.getType());
}
workingdayDAO.save(wd);
var error = workingdayService.upsertWorkingday(wd);
if(error != null) {
throw new ResponseStatusException(INTERNAL_SERVER_ERROR, error.getMessage());
}
}

@GetMapping(produces = APPLICATION_JSON_VALUE)
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/tb/dailyreport/service/TimereportService.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.tb.dailyreport.domain.TimereportDTO;
import org.tb.dailyreport.domain.WorkingDayValidationError;
import org.tb.dailyreport.domain.Workingday;
import org.tb.dailyreport.domain.Workingday.WorkingDayType;
import org.tb.dailyreport.persistence.PublicholidayDAO;
import org.tb.dailyreport.persistence.ReferencedayDAO;
import org.tb.dailyreport.persistence.TimereportDAO;
Expand Down Expand Up @@ -279,6 +280,7 @@ private void checkAndSaveTimereports(AuthorizedUser authorizedUser, List<Timerep
timereports.forEach(t -> log.debug("checking Timereport {}", t.getTimeReportAsString()));

checkAuthorization(timereports, authorizedUser);
validateWorkingDayBusinessRules(timereports);
validateTimeReportingBusinessRules(timereports);
validateContractBusinessRules(timereports);
validateOrderBusinessRules(timereports);
Expand Down Expand Up @@ -507,6 +509,16 @@ private void validateContractBusinessRules(List<Timereport> timereports) throws
TR_EMPLOYEE_CONTRACT_INVALID_REF_DATE);
}

private void validateWorkingDayBusinessRules(List<Timereport> timereports) {
timereports.forEach(timereport -> {
var workingDay = workingdayDAO.getWorkingdayByDateAndEmployeeContractId(
timereport.getReferenceday().getRefdate(),
timereport.getEmployeecontract().getId()
);
DataValidation.isTrue(workingDay.getType() != WorkingDayType.NOT_WORKED, TR_WORKING_DAY_NOT_WORKED);
});
}

private void validateTimeReportingBusinessRules(List<Timereport> timereports) {
timereports.forEach(timereport -> {
Year reportedYear = getYear(timereport.getReferenceday().getRefdate());
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/org/tb/dailyreport/service/WorkingdayService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.tb.dailyreport.service;

import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.tb.common.BusinessRuleChecks;
import org.tb.common.ErrorCode;
import org.tb.dailyreport.domain.WorkingDayValidationError;
import org.tb.dailyreport.domain.Workingday;
import org.tb.dailyreport.domain.Workingday.WorkingDayType;
import org.tb.dailyreport.persistence.TimereportDAO;
import org.tb.dailyreport.persistence.WorkingdayRepository;

@Service
@AllArgsConstructor
public class WorkingdayService {

private final WorkingdayRepository workingdayRepository;
private final TimereportDAO timereportDAO;

public WorkingDayValidationError upsertWorkingday(Workingday workingday) {
if(workingday.getType() == WorkingDayType.NOT_WORKED) {
var timereports = timereportDAO.getTimereportsByDateAndEmployeeContractId(workingday.getEmployeecontract().getId(), workingday.getRefday());
BusinessRuleChecks.empty(timereports, ErrorCode.WD_NOT_WORKED_TIMEREPORTS_FOUND);
}
workingdayRepository.save(workingday);
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,22 @@ public void addWorkingTime(Duration workingTime) {
public boolean isZeroWorkingTime() {
return workingTime == null || workingTime.isZero();
}

public boolean isNotWorked() {
return workingDayType == WorkingDayType.NOT_WORKED;
}

public boolean isPartiallyNotWorked() {
return workingDayType == WorkingDayType.PARTIALLY;
}

public Duration getEffectiveTargetTime() {
if(workingDayType == WorkingDayType.PARTIALLY) {
return workingTime;
}
if(workingDayType == WorkingDayType.NOT_WORKED) {
return Duration.ZERO;
}
return contractWorkingTime;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,8 @@ public Matrix createMatrix(LocalDate dateFirst, LocalDate dateLast, long employe
Duration totalOvertimeCompensation = null;

if(method == MATRIX_SPECIFICDATE_ALLORDERS_SPECIFICEMPLOYEES && employeecontract != null) {
//calculate dayhourstarget
var workdayCount = dayTotals.stream().filter(d -> !d.isPublicHoliday() && !d.isSatSun()).count();
totalWorkingTimeTarget = employeecontract.getDailyWorkingTime().multipliedBy(workdayCount);
//calculate dayhourstarget // TODO compare with OvertimeService
totalWorkingTimeTarget = dayTotals.stream().map(MatrixDayTotal::getEffectiveTargetTime).reduce(Duration.ZERO, Duration::plus);

// calculate overtime compensation
totalOvertimeCompensation = overtimeService.calculateOvertimeCompensation(employeecontract.getId(), dateFirst, dateLast);
Expand Down Expand Up @@ -227,13 +226,15 @@ private List<MatrixDayTotal> initializeDayTotals(long employeeContractId,
var dayOfWeek = dayTotal.getDate().getDayOfWeek();
if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY) {
dayTotal.setSatSun(true);
dayTotal.setContractWorkingTime(Duration.ZERO);
}
dayTotal.setWeekDay(WEEK_DAYS_MAP.get(dayOfWeek));

// mark public holidays
if (publicHolidayMap.containsKey(date)) {
dayTotal.setPublicHoliday(true);
dayTotal.setPublicHolidayName(publicHolidayMap.get(date).getName());
dayTotal.setContractWorkingTime(Duration.ZERO);
}

var workingDay = workingDays.get(date);
Expand Down
31 changes: 28 additions & 3 deletions src/main/java/org/tb/employee/service/OvertimeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.Getter;
Expand All @@ -24,8 +26,11 @@
import org.tb.common.util.DateUtils;
import org.tb.dailyreport.domain.Publicholiday;
import org.tb.dailyreport.domain.TimereportDTO;
import org.tb.dailyreport.domain.Workingday;
import org.tb.dailyreport.domain.Workingday.WorkingDayType;
import org.tb.dailyreport.persistence.PublicholidayDAO;
import org.tb.dailyreport.persistence.TimereportDAO;
import org.tb.dailyreport.persistence.WorkingdayDAO;
import org.tb.employee.domain.Employeecontract;
import org.tb.employee.domain.Overtime;
import org.tb.employee.domain.OvertimeReport;
Expand All @@ -38,12 +43,14 @@

@Service
@RequiredArgsConstructor
// FIXME move to dailyreport
public class OvertimeService {

private final EmployeecontractDAO employeecontractDAO;
private final PublicholidayDAO publicholidayDAO;
private final TimereportDAO timereportDAO;
private final OvertimeDAO overtimeDAO;
private final WorkingdayDAO workingdayDAO;

public Optional<Duration> calculateOvertime(long employeecontractId, LocalDate begin, LocalDate end) {
var employeecontract = employeecontractDAO.getEmployeeContractById(employeecontractId);
Expand Down Expand Up @@ -129,21 +136,39 @@ public Duration calculateOvertimeCompensation(long employeecontractId, LocalDate
)
.map(TimereportDTO::getReferenceday)
.distinct()
.collect(Collectors.toSet());
.collect(Collectors.toCollection(HashSet::new));

// add dates with not worked or partially worked days
var workingDays = workingdayDAO.getWorkingdaysByEmployeeContractId(employeecontractId, begin, end);
dates.addAll(
workingDays
.stream()
.filter(workingday -> workingday.getType() != WorkingDayType.WORKED)
.map(Workingday::getRefday)
.distinct()
.collect(Collectors.toSet())
);

// sum reported time for every date with an overtime compensation
var reportedMinutesPerDate = timereports
.stream()
.filter(timereport -> dates.contains(timereport.getReferenceday()))
.collect(Collectors.groupingBy(TimereportDTO::getReferenceday, Collectors.summingLong(timereport -> timereport.getDuration().toMinutes())));
reportedMinutesPerDate = new HashMap<>(reportedMinutesPerDate);
for(var date : dates) {
// ensure overtime is calculated for all dates, even if not time was logged - thats the case when WorkingDayType is used
if(!reportedMinutesPerDate.containsKey(date)) {
reportedMinutesPerDate.put(date, 0L);
}
}

var dailyWorkingTime = contract.getDailyWorkingTime();

var compensatedOvertime= reportedMinutesPerDate.values()
var compensatedOvertime = reportedMinutesPerDate.values()
.stream()
.map(Duration::ofMinutes)
.map(duration -> dailyWorkingTime.minus(duration))
.filter(duration -> !duration.isNegative())
.filter(duration -> duration.isPositive()) // just use positive compensations
.collect(Collectors.summingLong(Duration::toMinutes));

return Duration.ofMinutes(compensatedOvertime);
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/org/tb/web/MessageResources_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ errorcode.tr.0021=Datum passt nicht zum Mitarbeitervertrag
errorcode.tr.0022=Buchungen können nur im vergangenen, aktuellen oder nächsten Jahr vorgenommen bzw. geändert werden
errorcode.tr.0023=Überstundenausgleich kann nur mit einer Zeit von 00:00 gebucht werden
errorcode.tr.0024=An dem Tag muss zuerst ein Arbeitsbeginn eingetragen werden
errorcode.tr.0025=An dem Tag darf nicht 'Gearbeitet=nein' stehen.

errorcode.ec.0001=Buchungen gefunden, die außerhalb der neuen Gültigkeit des Mitarbeitervertrags sind.
errorcode.ec.0002=Bitte einen gültigen Zeitraum angeben
errorcode.ec.0003=Mitarbeiteraufträge gefunden, die sich nicht mit der neuen Gültigkeit dieses Mitarbeitervertrags vertragen.
errorcode.ec.0004=Ungültigen Abnehmer angegeben.
errorcode.ec.0005=Mitarbeitervertrag überschneidet sich zeitlich mit einem existierenden Vertrag.

errorcode.wd.0001=Es wurden Buchungen gefunden, bitte vorher löschen oder verschieben.

common.error.admin.required=Für diese Aktion sind Adminrechte erforderlich.

form.login.error.loginname.empty=Bitte User eingeben.
Expand Down Expand Up @@ -778,6 +781,7 @@ main.matrixoverview.headline.difference.text=Differenz:
main.matrixoverview.table.overall.text=GESAMT
main.matrixoverview.table.breakduration.text=Pausendauer
main.matrixoverview.table.startofwork.text=Arbeitsbeginn
main.matrixoverview.table.notworked.text=Nicht gearbeitet
main.matrixoverview.table.customerordernr.text=Auftrags-Nr
main.matrixoverview.table.subordernr.text=Auftrags-UnterNr
main.matrixoverview.table.sum.text=Summe
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/org/tb/web/MessageResources_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ errorcode.tr.0021=Date does not match the employee contract
errorcode.tr.0022=Bookings can only be modified in the last, current or next year
errorcode.tr.0023=Overtime compensations must always be booked with 00:00 time
errorcode.tr.0024=A start of work must first be entered on the day
errorcode.tr.0025=The working day must not have 'Worked=no'.

errorcode.ec.0001=Timereports found that have been reported and are outside of the new validity
errorcode.ec.0002=Please provide a valid date range
errorcode.ec.0003=Employee orders found that are outside of the new validity
errorcode.ec.0004=Invalid supervisor provided
errorcode.ec.0005=Employee contract validity overlaps another employee contract of the same employee

errorcode.wd.0001=Timereports found, please move or delete first!

common.error.admin.required=This action requires admin priveleges.

form.login.error.loginname.empty=Please enter a loginname.
Expand Down Expand Up @@ -777,6 +780,7 @@ main.matrixoverview.headline.difference.text=Difference:
main.matrixoverview.table.overall.text=Overall
main.matrixoverview.table.breakduration.text=Break duration
main.matrixoverview.table.startofwork.text=Start of work
main.matrixoverview.table.notworked.text=Not worked
main.matrixoverview.table.customerordernr.text=Customerorder-Nr
main.matrixoverview.table.subordernr.text=Suborder-Nr
main.matrixoverview.table.sum.text=Sum
Expand Down
Loading

0 comments on commit d0e8d7e

Please sign in to comment.