From bfd7091360752d30447bc1d3b74f8a36c5fc4b1c Mon Sep 17 00:00:00 2001 From: Kheireddine Mehdi Date: Mon, 18 Nov 2024 15:55:30 +0100 Subject: [PATCH] Update attendances module Add services files directory configuration to application.properties --- .../resources/domains/CompanyDepartment.xml | 2 +- .../apps/hr/job/FetchAttendanceJob.java | 9 +- .../apps/hr/job/FetchCheckInOutJob.java | 12 +- .../axelor/apps/hr/job/FetchHrTicketsJob.java | 24 + .../apps/hr/module/HumanResourceModule.java | 3 + .../apps/hr/service/AbsenceService.java | 5 +- .../apps/hr/service/AbsenceServiceImpl.java | 53 +- .../apps/hr/service/AuthorizationService.java | 3 + .../hr/service/AuthorizationServiceImpl.java | 154 +++++- .../apps/hr/service/DailyReportService.java | 9 + .../hr/service/DailyReportServiceImpl.java | 241 +++++++-- .../apps/hr/service/MonthlyReportService.java | 11 +- .../hr/service/MonthlyReportServiceImpl.java | 492 +++++++++++++++--- .../hr/service/employee/EmployeeService.java | 11 + .../service/employee/EmployeeServiceImpl.java | 43 ++ .../axelor/apps/hr/web/AbsenceController.java | 58 ++- .../apps/hr/web/AuthorizationController.java | 87 ++++ .../apps/hr/web/DailyReportController.java | 117 +++++ .../apps/hr/web/EmployeeController.java | 55 +- .../apps/hr/web/MonthlyReportController.java | 73 ++- .../src/main/resources/domains/Absence.xml | 69 ++- .../main/resources/domains/DailyReport.xml | 11 +- .../src/main/resources/domains/Employee.xml | 4 + .../resources/domains/EmployeeFingerPrint.xml | 15 + .../src/main/resources/domains/ExtraHours.xml | 1 + .../src/main/resources/domains/Shift.xml | 8 +- .../src/main/resources/i18n/messages_fr.csv | 1 + .../resources/views/EmployeeFingerPrint.xml | 24 + .../src/main/resources/views/Selects.xml | 46 ++ src/main/resources/application.properties | 12 +- 30 files changed, 1445 insertions(+), 208 deletions(-) create mode 100644 modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/EmployeeFingerPrint.xml create mode 100644 modules/axelor-open-suite/axelor-human-resource/src/main/resources/views/EmployeeFingerPrint.xml diff --git a/modules/axelor-open-suite/axelor-base/src/main/resources/domains/CompanyDepartment.xml b/modules/axelor-open-suite/axelor-base/src/main/resources/domains/CompanyDepartment.xml index 8de0bd9..7a9e51d 100644 --- a/modules/axelor-open-suite/axelor-base/src/main/resources/domains/CompanyDepartment.xml +++ b/modules/axelor-open-suite/axelor-base/src/main/resources/domains/CompanyDepartment.xml @@ -9,8 +9,8 @@ + - diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchAttendanceJob.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchAttendanceJob.java index 1f4b1e0..98e0dcf 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchAttendanceJob.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchAttendanceJob.java @@ -10,10 +10,12 @@ import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.quartz.SchedulerException; +import com.axelor.app.AppSettings; public class FetchAttendanceJob implements Job { private final Logger log = LoggerFactory.getLogger(FetchCheckInOutJob.class); + private final String pythonScriptDir = AppSettings.get().get("services.dir"); @Override public void execute(JobExecutionContext context) throws JobExecutionException{ @@ -22,9 +24,14 @@ public class FetchAttendanceJob implements Job { return; } + if (pythonScriptDir == null || pythonScriptDir.isEmpty()) { + log.error("Python script path is not configured in AppSettings."); + return; + } + try { // Define the command to run the Python script with the correct path (V3) - String[] args = {"python", "C:\\Users\\administrator\\Desktop\\Scrape\\main.py"}; + String[] args = {"python", pythonScriptDir + "\\Scrape\\main.py"}; // Execute the command Process p = Runtime.getRuntime().exec(args); diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchCheckInOutJob.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchCheckInOutJob.java index 4f05438..d340235 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchCheckInOutJob.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchCheckInOutJob.java @@ -14,10 +14,12 @@ import org.slf4j.LoggerFactory; import com.axelor.inject.Beans; import java.time.LocalDate; import org.quartz.SchedulerException; +import com.axelor.app.AppSettings; public class FetchCheckInOutJob implements Job { private final Logger log = LoggerFactory.getLogger(FetchCheckInOutJob.class); + private final String pythonScriptDir = AppSettings.get().get("services.dir"); @Override public void execute(JobExecutionContext context) throws JobExecutionException { @@ -26,6 +28,11 @@ public class FetchCheckInOutJob implements Job { return; } + if (pythonScriptDir == null || pythonScriptDir.isEmpty()) { + log.error("Python script path is not configured in AppSettings."); + return; + } + try { LocalDate today = LocalDate.now(); @@ -35,11 +42,10 @@ public class FetchCheckInOutJob implements Job { .all() .filter("self.date_attendance = :today") .bind("today", today) - .fetch() - .size(); + .fetch().size(); // Define the command to run the Python script with the correct path (V3) - String[] args = {"python", "C:\\Users\\administrator\\Desktop\\Scrape\\main2.py", String.valueOf(lenCheckInOutList)}; + String[] args = {"python", pythonScriptDir + "\\Scrape\\main2.py", String.valueOf(lenCheckInOutList)}; // Execute the command Process p = Runtime.getRuntime().exec(args); diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchHrTicketsJob.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchHrTicketsJob.java index 023bd3c..0ebe68a 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchHrTicketsJob.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchHrTicketsJob.java @@ -155,6 +155,30 @@ public class FetchHrTicketsJob implements Job { } }); + executorService.submit( + () -> { + try { + log.info("Fetching authorization AP..."); + String jsonResponse = Beans.get(AuthorizationController.class).fetchAP(token); + log.info("Fetched authorization AP successfully."); + if (jsonResponse != null) { + try { + JSONArray jsonArray = new JSONArray(jsonResponse); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + Beans.get(AuthorizationService.class).saveAP(jsonObject); + } + } catch (JSONException e) { + log.error("Failed to parse JSON: " + jsonResponse, e); + } + } else { + log.warn("No response received from fetchAP."); + } + } catch (Exception e) { + log.error("Failed to fetch Salary authorization: ", e); + } + }); + // Leave requests fetch task executorService.submit( () -> { diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/module/HumanResourceModule.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/module/HumanResourceModule.java index ec9da5e..db12c44 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/module/HumanResourceModule.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/module/HumanResourceModule.java @@ -59,6 +59,8 @@ import com.axelor.apps.hr.service.extra.hours.ExtraHoursService; import com.axelor.apps.hr.service.extra.hours.ExtraHoursServiceImpl; import com.axelor.apps.hr.service.leave.LeaveService; import com.axelor.apps.hr.service.leave.LeaveServiceImpl; +import com.axelor.apps.hr.service.DailyReportService; +import com.axelor.apps.hr.service.DailyReportServiceImpl; import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherAdvanceService; import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherAdvanceServiceImpl; import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherMgtLineService; @@ -117,5 +119,6 @@ public class HumanResourceModule extends AxelorModule { bind(PartnerAccountRepository.class).to(PartnerHRRepository.class); bind(BankOrderMergeServiceImpl.class).to(BankOrderMergeHRServiceImpl.class); bind(TimesheetReportService.class).to(TimesheetReportServiceImpl.class); + bind(DailyReportService.class).to(DailyReportServiceImpl.class); } } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceService.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceService.java index dfc6056..c3bb5a1 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceService.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceService.java @@ -13,12 +13,13 @@ public interface AbsenceService { void attachTheAbsenceWithDailyReport(Absence absence, List dailyreports); @Transactional - void deAttachTheAbsenceWithDailyReport(Absence absence, List dailyreports); + void deAttachTheAbsenceWithDailyReport(Absence absence, List dailyreports, Boolean archive); @Transactional - void chooseAbsenceType(Absence absence, String selectedType); + void chooseAbsenceType(Absence absence, Integer selectedType); @Transactional BigDecimal calculateTotalAbsenceHours(LocalDateTime absenceStartDate, LocalDateTime absenceEndDate); + BigDecimal calculateTotalAbsenceMinutes(LocalDateTime absenceStartDate, LocalDateTime absenceEndDate); } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceServiceImpl.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceServiceImpl.java index 442125d..1f4f542 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceServiceImpl.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceServiceImpl.java @@ -18,6 +18,8 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.time.Duration; +import java.math.RoundingMode; public class AbsenceServiceImpl implements AbsenceService { @@ -41,27 +43,29 @@ public class AbsenceServiceImpl implements AbsenceService { Absence absence, List dailyreports) { // Iterate over each DailyReport in the list for (DailyReport dailyreport : dailyreports) { - dailyreport.setAbsence(absence); // Set the absence for each report + dailyreport.addAbsenceSetItem(absence); // Set the absence for each report dailyReportRepository.save(dailyreport); } } @Transactional - public void deAttachTheAbsenceWithDailyReport(Absence absence, List dailyreports) { + public void deAttachTheAbsenceWithDailyReport(Absence absence, List dailyreports, Boolean archive) { if(dailyreports != null){ // Iterate over each DailyReport in the list for (DailyReport dailyreport : dailyreports) { - dailyreport.setAbsence(null); // Set the absence for each report + dailyreport.removeAbsenceSetItem(null); // Set the absence for each report dailyReportRepository.save(dailyreport); } } - - absence.setArchived(true); - absenceRepository.save(absence); + + if (archive) { + absence.setArchived(true); + absenceRepository.save(absence); + } } @Transactional - public void chooseAbsenceType(Absence absence, String selectedType) { + public void chooseAbsenceType(Absence absence, Integer selectedType) { // Set the selected absence type to the absence entity // Assuming you have a field named 'typeAbsence' in the Absence entity absence.setAbsenceType(selectedType); @@ -101,6 +105,17 @@ public class AbsenceServiceImpl implements AbsenceService { return totalAbsenceHours; } + public BigDecimal calculateTotalAbsenceMinutes(LocalDateTime absenceStartDate, LocalDateTime absenceEndDate) { + // Calculate the duration between the two LocalDateTime objects + Duration duration = Duration.between(absenceStartDate, absenceEndDate); + + // Convert the duration to minutes and then divide by 60 to get hours + long minutes = duration.toMinutes(); + BigDecimal hours = BigDecimal.valueOf(minutes).divide(BigDecimal.valueOf(60), 2, BigDecimal.ROUND_HALF_UP); + + return customRound(hours); + } + private boolean isSpecialOvertimeDay(LocalDate date) { for (EventsPlanningLine line : eventsPlanningLines) { if (line.getDate().equals(date)) { @@ -109,4 +124,28 @@ public class AbsenceServiceImpl implements AbsenceService { } return false; } + + public static BigDecimal customRound(BigDecimal value) { + // Get the fractional part of the number + BigDecimal fractionalPart = value.remainder(BigDecimal.ONE); + + // Define the intervals and their corresponding rounded values + if (fractionalPart.compareTo(new BigDecimal("0.125")) < 0) { + return value.setScale(0, RoundingMode.DOWN); + } else if (fractionalPart.compareTo(new BigDecimal("0.125")) >= 0 + && fractionalPart.compareTo(new BigDecimal("0.375")) < 0) { + return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.25")); + } else if (fractionalPart.compareTo(new BigDecimal("0.375")) >= 0 + && fractionalPart.compareTo(new BigDecimal("0.625")) < 0) { + return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.50")); + } else if (fractionalPart.compareTo(new BigDecimal("0.625")) >= 0 + && fractionalPart.compareTo(new BigDecimal("0.875")) < 0) { + return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.75")); + } else if (fractionalPart.compareTo(new BigDecimal("0.875")) >= 0) { + return value.setScale(0, RoundingMode.UP); + } else { + return value; // In case no rounding is needed + } + } + } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationService.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationService.java index 2c58355..836f50d 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationService.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationService.java @@ -14,4 +14,7 @@ public interface AuthorizationService { @Transactional(rollbackOn = {Exception.class}) public void saveBS(JSONObject jsonObject) throws AxelorException; + + @Transactional(rollbackOn = {Exception.class}) + public void saveAP(JSONObject jsonObject) throws AxelorException; } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationServiceImpl.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationServiceImpl.java index 8aa7dd0..3acdc42 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationServiceImpl.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationServiceImpl.java @@ -281,7 +281,7 @@ public class AuthorizationServiceImpl implements AuthorizationService { .fetchOne(); if (dailyReport != null) { - dailyReport.setIsAuthorizedAbsence(true); + dailyReport.setIsAuthorizedLateArrival(true); } } else if (previousStatus == 2 && newStatus == 4) { System.out.println("Tickets :" + idInt + " Status changed from " + previousStatus + " to " + newStatus); @@ -321,7 +321,7 @@ public class AuthorizationServiceImpl implements AuthorizationService { if (dailyReport != null) { authorization.setDailyReport(dailyReport); if(validation_status == 3) - dailyReport.setIsAuthorizedAbsence(true); + dailyReport.setIsAuthorizedLateArrival(true); } // Save the new Authorization authorizationRepository.save(authorization); @@ -338,6 +338,156 @@ public class AuthorizationServiceImpl implements AuthorizationService { @Transactional(rollbackOn = {Exception.class}) public void saveBS(JSONObject jsonObject) throws AxelorException { + try { + // Extract fields + int idInt = jsonObject.getInt("id"); + String id = Integer.toString(idInt); + String ticketLink = "https://dsi.sophal.dz/front/ticket.form.php?id=" + id; + String matricule = jsonObject.getString("matricule"); + String date_absence = jsonObject.getString("date_sortie"); + String commentaire = jsonObject.getString("motif"); + String heure_sortie = jsonObject.getString("heure_sortie"); + int validation_status = jsonObject.optInt("validation_status",2); + String validateByUser = jsonObject.optString("validate_by_user",null); + String dateValidation = jsonObject.optString("validation_date",null); + + // GET EMPLOYEES + Employee employee = employeeRepo + .all() + .filter("self.registrationNumber = :matricule") + .bind("matricule", matricule) + .fetchOne(); + + if (employee == null) { + System.err.println("Employee with matricule " + matricule + " not found."); + return; + } + + Employee validatedByEmployee = null; + + if(validateByUser != null){ + validatedByEmployee = employeeRepo + .all() + .filter("self.registrationNumber = :matricule") + .bind("matricule", validateByUser) + .fetchOne(); + + if (validatedByEmployee == null) { + System.err.println("Validator employee with matricule " + validateByUser + " not found."); + return; + } + } + + // Parse validation date (handle null case) + LocalDate validationDate = null; + if (dateValidation != null && !dateValidation.isEmpty()) { + try { + OffsetDateTime offsetDateTime = OffsetDateTime.parse(dateValidation, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + validationDate = offsetDateTime.toLocalDate(); // Extract only the date part + } catch (DateTimeParseException e) { + System.out.println("Error parsing dateValidation: " + dateValidation); + validationDate = null; + } + } + + // Parse Requisition Date + LocalDate absenceDate; + try { + absenceDate = LocalDate.parse(date_absence); + } catch (DateTimeParseException e) { + System.out.println("Error parsing date_absence: " + date_absence); + absenceDate = null; + } + + // Check if Authorization exists by ticketId + Authorization authorization = authorizationRepository + .all() + .filter("self.ticketId = :ticketId") + .bind("ticketId", idInt) + .fetchOne(); + + if (authorization != null) { + // Authorization exists, compare previous and new status + int previousStatus = authorization.getStatusSelect(); // Previous status + int newStatus = validation_status; // New status + + if (previousStatus == 2 && newStatus == 3) { + System.out.println("Tickets :" + idInt + " Status changed from " + previousStatus + " to " + newStatus); + // Update the fields of the existing Authorization + authorization.setValidatedByEmployee(validatedByEmployee); + authorization.setValidationDate(validationDate); + authorization.setStatusSelect(newStatus); + // Save the updated Authorization + authorizationRepository.save(authorization); + + // Get Daily report + DailyReport dailyReport = + dailyReportRepo + .all() + .filter("self.employee = :employee and self.reportDate = :reportDate") + .bind("employee", employee) + .bind("reportDate", absenceDate) + .fetchOne(); + + if (dailyReport != null) { + dailyReport.setIsAuthorizedEarlyDeparture(true); + } + + } else if (previousStatus == 2 && newStatus == 4) { + System.out.println("Tickets :" + idInt + " Status changed from " + previousStatus + " to " + newStatus); + authorization.setRefusedByEmployee(validatedByEmployee); + authorization.setRefusalDate(validationDate); + authorization.setStatusSelect(newStatus); + // Save the updated Authorization + authorizationRepository.save(authorization); + } + } else { + String updatedDescription = commentaire + " - " + heure_sortie; + // Create an instance of Authorization + authorization = new Authorization(); + authorization.setEmployee(employee); + authorization.setTicketId(idInt); + authorization.setTicket(ticketLink); + authorization.setRequisitionDate(absenceDate); + authorization.setDescription(updatedDescription); + authorization.setStatusSelect(validation_status); + authorization.setAuthorizationType(0); + + if (validation_status == 3) { + authorization.setValidatedByEmployee(validatedByEmployee); + authorization.setValidationDate(validationDate); + } else if (validation_status == 4) { + authorization.setRefusedByEmployee(validatedByEmployee); + authorization.setRefusalDate(validationDate); + } + + // Get Daily report + DailyReport dailyReport = dailyReportRepo + .all() + .filter("self.employee = :employee and self.reportDate = :reportDate") + .bind("employee", employee) + .bind("reportDate", absenceDate) + .fetchOne(); + + if (dailyReport != null) { + authorization.setDailyReport(dailyReport); + if(validation_status == 3) + dailyReport.setIsAuthorizedEarlyDeparture(true); + } + // Save the new Authorization + authorizationRepository.save(authorization); + } + } catch (JSONException e) { + System.err.println("Failed to parse JSON: " + jsonObject.toString()); + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Transactional(rollbackOn = {Exception.class}) + public void saveAP(JSONObject jsonObject) throws AxelorException { + try { // Extract fields int idInt = jsonObject.getInt("id"); diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportService.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportService.java index db65660..2c879ca 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportService.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportService.java @@ -2,6 +2,9 @@ package com.axelor.apps.hr.service; import com.axelor.apps.hr.db.DailyReport; import com.google.inject.persist.Transactional; +import com.axelor.exception.AxelorException; +import java.math.BigDecimal; +import java.util.List; public interface DailyReportService { @@ -10,4 +13,10 @@ public interface DailyReportService { @Transactional void determineShift(DailyReport DailyReport); + + @Transactional(rollbackOn = {Exception.class}) + public void deducePrimes(DailyReport DailyReport, Integer primeSelection, BigDecimal valueToDeduce) throws AxelorException ; + + @Transactional(rollbackOn = {Exception.class}) + public void massDeducePrimes(List DailyReportList, Integer primeSelection, BigDecimal valueToDeduce) throws AxelorException; } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportServiceImpl.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportServiceImpl.java index 30a4599..a5f7725 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportServiceImpl.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportServiceImpl.java @@ -15,8 +15,10 @@ import com.axelor.apps.hr.db.repo.AuthorizationRepository; import com.axelor.apps.hr.db.repo.OffDayWorkRepository; import com.axelor.apps.hr.db.repo.DailyReportRepository; import com.axelor.apps.hr.db.repo.ShiftRepository; +import com.axelor.apps.hr.service.AbsenceServiceImpl; import com.axelor.inject.Beans; import com.google.inject.persist.Transactional; +import com.axelor.exception.AxelorException; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.DayOfWeek; @@ -26,6 +28,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Year; import java.util.List; +import java.util.ArrayList; import com.google.inject.Inject; public class DailyReportServiceImpl implements DailyReportService { @@ -85,14 +88,14 @@ public class DailyReportServiceImpl implements DailyReportService { LocalDate reportDate = dailyReport.getReportDate(); Integer shift = dailyReport.getShift().getShift(); - LocalTime shiftStartHour = null, shiftEndHour = null; + LocalDateTime firstEnter, lastQuit; + + LocalTime shiftStartHour = null, shiftEndHour = null, shiftStartPauseHour = null, shiftEndPauseHour = null; if(shift != null){ - System.out.println("rapporurnalier: ????????????????"+ dailyReport.getId()); shiftStartHour = dailyReport.getShift().getStartHour(); shiftEndHour = dailyReport.getShift().getEndHour(); - System.out.println("Shift: "+ shift); - System.out.println("shiftStartHour: "+ shiftStartHour); - System.out.println("shiftENDHour: "+ shiftEndHour); + shiftStartPauseHour = dailyReport.getShift().getStartPause(); + shiftEndPauseHour = dailyReport.getShift().getEndPause(); } if (enters[0] != null && quits[0] != null) { @@ -100,9 +103,9 @@ public class DailyReportServiceImpl implements DailyReportService { Duration totalDuration = Duration.ZERO; Duration totalSupDuration = Duration.ZERO; Duration nightDuration = Duration.ZERO; - Duration pauseDuration = Duration.ZERO; - LocalDateTime firstEnter = enters[0]; - LocalDateTime lastQuit = quits[0]; + Duration breakDuration = Duration.ZERO; + firstEnter = enters[0]; + lastQuit = quits[0]; for (int i = 0; i < enters.length; i++) { if (enters[i] != null && quits[i] != null) { @@ -114,6 +117,7 @@ public class DailyReportServiceImpl implements DailyReportService { dailyReport.setLastQuit(lastQuit); LocalTime firstEnterTime = firstEnter.toLocalTime(); LocalTime lastQuitTime = lastQuit.toLocalTime(); + // Calculate late arrival if firstEnter is later than shift start if(shiftStartHour != null){ if (firstEnterTime.isAfter(shiftStartHour)) { @@ -136,19 +140,24 @@ public class DailyReportServiceImpl implements DailyReportService { totalDuration = totalDuration.plus(Duration.between(firstEnter, lastQuit)); long totalMinutes = totalDuration.toMinutes(); BigDecimal totalHours = BigDecimal.valueOf(totalMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); - dailyReport.setWorkHours(customRound(totalHours)); + dailyReport.setWorkHours(totalHours); // Calculate night hours nightDuration = calculateNightDuration(firstEnter, lastQuit); long totalNightMinutes = nightDuration.toMinutes(); BigDecimal totalNightHours = BigDecimal.valueOf(totalNightMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); - dailyReport.setNightHours(customRound(totalNightHours)); + dailyReport.setNightHours(totalNightHours); - // Pause Hours - pauseDuration = calculatePauseDuration(enters, quits, employee); - long pauseMinutes = pauseDuration.toMinutes(); - BigDecimal pauseHours = BigDecimal.valueOf(pauseMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); - dailyReport.setBreakHours(customRound(pauseHours)); + // Break Hours + breakDuration = calculateBreakDuration(enters, quits); + long breakMinutes = breakDuration.toMinutes(); + BigDecimal breakHours = BigDecimal.valueOf(breakMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + dailyReport.setBreakHours(breakHours); + + if(shiftStartPauseHour != null && shiftEndPauseHour != null){ + boolean allInAllowedRange = areBreaksInAllowedRange(enters, quits, shiftStartPauseHour, shiftEndPauseHour); + dailyReport.setBreakNotInTheAllowedRange(allInAllowedRange); + } // shift 2 if (shift == 2) { @@ -202,8 +211,8 @@ public class DailyReportServiceImpl implements DailyReportService { dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); } - dailyReport.setExtraHours100(customRound(extraHours100)); - dailyReport.setExtraHours50(customRound(extraHours50)); + dailyReport.setExtraHours100(extraHours100); + dailyReport.setExtraHours50(extraHours50); // other shifts } else { @@ -216,11 +225,11 @@ public class DailyReportServiceImpl implements DailyReportService { if (firstEnter.getDayOfWeek() == DayOfWeek.SATURDAY) { if(shift == 0 || shift == 3){ - dailyReport.setExtraHours50(customRound(totalHours).subtract(customRound(totalNightHours))); - dailyReport.setExtraHours100(customRound(totalNightHours)); + dailyReport.setExtraHours50(totalHours.subtract(totalNightHours)); + dailyReport.setExtraHours100(totalNightHours); dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); } else { - dailyReport.setExtraHours50(customRound(totalHours)); + dailyReport.setExtraHours50(totalHours); dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); } @@ -229,18 +238,18 @@ public class DailyReportServiceImpl implements DailyReportService { // Add recup if (totalHours.compareTo(BigDecimal.valueOf(6)) >= 0) createOffDayWork(reportDate, employee); - dailyReport.setExtraHours100(customRound(totalHours)); + dailyReport.setExtraHours100(totalHours); dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); } else { if(shift == 0 || shift == 3){ - dailyReport.setExtraHours50(customRound(totalSupHours).subtract(customRound(totalNightHours))); - dailyReport.setExtraHours100(customRound(totalNightHours)); + dailyReport.setExtraHours50(totalSupHours.subtract(totalNightHours)); + dailyReport.setExtraHours100(totalNightHours); dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); dailyReport.setAllowanceRecall(totalSupHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); } else { - dailyReport.setExtraHours50(customRound(totalSupHours)); + dailyReport.setExtraHours50(totalSupHours); dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); dailyReport.setAllowanceRecall(totalSupHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); } @@ -248,23 +257,25 @@ public class DailyReportServiceImpl implements DailyReportService { } // Calculate ITP - dailyReport.setItp(customRound(calculateItp(totalHours, shift,dailyReport.getHasItp()))); + dailyReport.setItp(calculateItp(totalHours, shift,dailyReport.getHasItp())); dailyReport.setIsCalculated(true); } else if (enters[0] != null && quits[0] == null) { // When the employee registers attendance only once - dailyReport.setWorkHours(BigDecimal.valueOf(8)); - dailyReport.setAbsenceHours(BigDecimal.valueOf(0)); - dailyReport.setIsCalculated(true); + if(shift!=2){ + dailyReport.setWorkHours(BigDecimal.valueOf(8)); + dailyReport.setAbsenceHours(BigDecimal.valueOf(0)); + dailyReport.setIsCalculated(true); + } } else if (enters.length != quits.length) { Duration totalDuration = Duration.ZERO; - LocalDateTime firstEnter = enters[0]; - LocalDateTime lastQuit = quits[quits.length - 1]; + firstEnter = enters[0]; + lastQuit = quits[quits.length - 1]; totalDuration = totalDuration.plus(Duration.between(firstEnter, lastQuit)); long totalMinutes = totalDuration.toMinutes(); BigDecimal totalHours = BigDecimal.valueOf(totalMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); - dailyReport.setWorkHours(customRound(totalHours)); + dailyReport.setWorkHours(totalHours); dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); dailyReport.setLastQuit(lastQuit); @@ -288,6 +299,18 @@ public class DailyReportServiceImpl implements DailyReportService { System.err.println("Warning: Employee doesn't exist."); dailyReport.setIsCalculated(true); } else { + dailyReport.setWorkHours(BigDecimal.valueOf(0)); + dailyReport.setNightHours(BigDecimal.valueOf(0)); + dailyReport.setBreakHours(BigDecimal.valueOf(0)); + dailyReport.setExtraHours50(BigDecimal.valueOf(0)); + dailyReport.setExtraHours100(BigDecimal.valueOf(0)); + dailyReport.setItp(BigDecimal.valueOf(0)); + dailyReport.setLateArrival(BigDecimal.valueOf(0)); + dailyReport.setEarlyDeparture(BigDecimal.valueOf(0)); + dailyReport.setAllowance(0); + dailyReport.setAllowanceRecall(0); + dailyReport.setBreakNotInTheAllowedRange(false); + dailyReport.setShift(Beans.get(ShiftRepository.class).find(50L)); System.err.println("Warning: no attendances for this day."); dailyReport.setIsCalculated(true); } @@ -296,7 +319,7 @@ public class DailyReportServiceImpl implements DailyReportService { if (dailyReport.getAbsence() == null) { Absence absence = Beans.get(AbsenceRepository.class) .all() - .filter("self.employee = :employee and self.startDate <= :reportDate and self.endDate >= :reportDate") + .filter("self.employee = :employee and self.startDate <= :reportDate and self.endDate >= :reportDate and (archived = false or archived is null)") .bind("employee", employee) .bind("reportDate",reportDate) // Changed from absenceStartDate and absenceEndDate to reportDate .fetchOne(); @@ -307,17 +330,34 @@ public class DailyReportServiceImpl implements DailyReportService { } // Authorization - if(dailyReport.getAuthorizationList() == null){ - Authorization authorization = Beans.get(AuthorizationRepository.class) - .all() - .filter("self.employee = :employee and self.requisitionDate = :reportDate") - .bind("employee",employee) - .bind("reportDate",reportDate) - .fetchOne(); - if (authorization != null) { - authorization.setDailyReport(dailyReport); - if(authorization.getStatusSelect() == 3) - dailyReport.setIsAuthorizedAbsence(true); + if (dailyReport.getAuthorizationList() == null) { + List authorizations = Beans.get(AuthorizationRepository.class) + .all() + .filter("self.employee = :employee and self.requisitionDate = :reportDate") + .bind("employee", employee) + .bind("reportDate", reportDate) + .fetch(); + + if (authorizations != null) { + List authorizationList = new ArrayList<>(); // Create a new list for authorizations + + for (Authorization authorization : authorizations) { + authorization.setDailyReport(dailyReport); + authorizationList.add(authorization); // Add each authorization to the list + + // Check authorization type and set corresponding flags in dailyReport + if (authorization.getAuthorizationType() == 0 && authorization.getStatusSelect() == 3) { + dailyReport.setIsAuthorizedEarlyDeparture(true); + } + if (authorization.getAuthorizationType() == 1 && authorization.getStatusSelect() == 3) { + dailyReport.setIsAuthorizedLateArrival(true); + } + if (authorization.getAuthorizationType() == 2 && authorization.getStatusSelect() == 3) { + dailyReport.setIsAuthorizedAbsence(true); + } + } + // Set the authorization list to dailyReport + dailyReport.setAuthorizationList(authorizationList); } } @@ -333,7 +373,56 @@ public class DailyReportServiceImpl implements DailyReportService { // Absence Hours if (!(dailyReport.getIsWeekend() || dailyReport.getIsFerieDay()) && dailyReport.getWorkHours().compareTo(BigDecimal.valueOf(8)) < 0) { - dailyReport.setAbsenceHours(customRound(BigDecimal.valueOf(8).subtract(dailyReport.getWorkHours()))); + dailyReport.setAbsenceHours(BigDecimal.valueOf(8).subtract(dailyReport.getWorkHours())); + // Create Absence AI + if(dailyReport.getAbsenceSet().isEmpty()){ + Boolean isAuthorizedAbsence = dailyReport.getIsAuthorizedAbsence(); + Boolean isAuthorizedLateArrival = dailyReport.getIsAuthorizedLateArrival(); + Boolean isAuthorizedEarlyDeparture = dailyReport.getIsAuthorizedEarlyDeparture(); + // AI all day + if(dailyReport.getAbsenceHours().compareTo(new BigDecimal("8")) == 0){ + if(!isAuthorizedAbsence){ + Absence absence = new Absence(); + absence.setEmployee(employee); + absence.setAbsenceType(19); // Absence irrégulière + absence.setStartDate(reportDate.atStartOfDay()); + absence.setEndDate(reportDate.atStartOfDay()); + BigDecimal totalAbsenceHours = Beans.get(AbsenceServiceImpl.class).calculateTotalAbsenceHours(reportDate.atStartOfDay(),reportDate.atStartOfDay()); + absence.setTotalAbsenceHours(totalAbsenceHours); + absenceRepository.save(absence); + dailyReport.addAbsenceSetItem(absence); + } + }else{ + if(dailyReport.getShift().getMaxTimeLateArrival() != null){ // to check that is different to shift N/A + LocalTime firstEnterTime = dailyReport.getEnter1().toLocalTime(); + if(firstEnterTime.isAfter(dailyReport.getShift().getMaxTimeLateArrival()) && !isAuthorizedLateArrival){ + Absence absence = new Absence(); + absence.setEmployee(employee); + absence.setAbsenceType(20); // Retard irrégulier + absence.setStartDate(reportDate.atTime(shiftStartHour)); + absence.setEndDate(dailyReport.getEnter1()); + BigDecimal totalAbsenceHours = Beans.get(AbsenceServiceImpl.class).calculateTotalAbsenceMinutes(reportDate.atTime(shiftStartHour),dailyReport.getEnter1()); + absence.setTotalAbsenceHours(totalAbsenceHours); + absenceRepository.save(absence); + dailyReport.addAbsenceSetItem(absence); + } + if(dailyReport.getLastQuit() != null){ + LocalTime lastQuitTime = dailyReport.getLastQuit().toLocalTime(); + if(lastQuitTime.isBefore(dailyReport.getShift().getMaxTimeEarlyDeparture()) && !isAuthorizedEarlyDeparture){ + Absence absence = new Absence(); + absence.setEmployee(employee); + absence.setAbsenceType(21); // Départ irrégulier + absence.setStartDate(dailyReport.getLastQuit()); + absence.setEndDate(reportDate.atTime(shiftEndHour)); + BigDecimal totalAbsenceHours = Beans.get(AbsenceServiceImpl.class).calculateTotalAbsenceMinutes(dailyReport.getLastQuit(),reportDate.atTime(shiftEndHour)); + absence.setTotalAbsenceHours(totalAbsenceHours); + absenceRepository.save(absence); + dailyReport.addAbsenceSetItem(absence); + } + } + } + } + } } else { dailyReport.setAbsenceHours(BigDecimal.ZERO); } @@ -449,24 +538,40 @@ public class DailyReportServiceImpl implements DailyReportService { return supDuration; } - private Duration calculatePauseDuration( - LocalDateTime[] enters, LocalDateTime[] quits, Employee employee) { + private Duration calculateBreakDuration(LocalDateTime[] enters, LocalDateTime[] quits) { - Duration pauseDuration = Duration.ZERO; + Duration breakDuration = Duration.ZERO; for (int i = 1; i < quits.length; i++) { if (enters[i] != null && quits[i - 1] != null) { - pauseDuration = pauseDuration.plus(Duration.between(quits[i - 1], enters[i])); + breakDuration = breakDuration.plus(Duration.between(quits[i - 1], enters[i])); } } - return pauseDuration; + return breakDuration; + } + + private boolean areBreaksInAllowedRange(LocalDateTime[] enters, LocalDateTime[] quits, LocalTime allowedStartTime, LocalTime allowedEndTime) { + + for (int i = 1; i < quits.length; i++) { + if (enters[i] != null && quits[i - 1] != null) { + LocalTime breakStartTime = quits[i - 1].toLocalTime(); + LocalTime breakEndTime = enters[i].toLocalTime(); + + // Check if the break falls outside the allowed range + if (breakStartTime.isBefore(allowedStartTime) || breakEndTime.isAfter(allowedEndTime)) { + return false; + } + } + } + + return true; } private BigDecimal calculateItp(BigDecimal totalHours, Integer shift, Boolean hasItp) { // Shift 0 (no itp) - if (hasItp == false) - return BigDecimal.ZERO; - else + if (hasItp == true && shift != 0) return totalHours.min(BigDecimal.valueOf(8)); + else + return BigDecimal.ZERO; } private void createOffDayWork(LocalDate reportDate, Employee employee) { @@ -544,5 +649,37 @@ public class DailyReportServiceImpl implements DailyReportService { return value; // In case no rounding is needed } } + + @Transactional(rollbackOn = {Exception.class}) + public void deducePrimes(DailyReport dailyReport, Integer primeSelection, BigDecimal valueToDeduce) throws AxelorException { + switch (primeSelection) { + case 1: // ITP + dailyReport.setDeduceItp(true); + dailyReport.setItpToDeduce(valueToDeduce); + break; + case 2: // Nuissance + dailyReport.setDeduceNuissance(true); + dailyReport.setItpToDeduce(valueToDeduce); + break; + case 3: // HS 50 + dailyReport.setDeduceSupHours50(true); + dailyReport.setSupHours50ToDeduce(valueToDeduce); + break; + case 4: // HS 100 + dailyReport.setDeduceSupHours100(true); + dailyReport.setSupHours100ToDeduce(valueToDeduce); + break; + default: + return; // Invalid configSelect, stop processing + } + Beans.get(DailyReportRepository.class).save(dailyReport); + } + + @Transactional(rollbackOn = {Exception.class}) + public void massDeducePrimes(List dailyReportList, Integer primeSelection, BigDecimal valueToDeduce) throws AxelorException { + for (DailyReport dailyReport : dailyReportList) { + this.deducePrimes(dailyReport, primeSelection, valueToDeduce); + } + } } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportService.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportService.java index b347b09..4781da2 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportService.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportService.java @@ -4,6 +4,7 @@ import com.axelor.apps.base.db.Period; import com.axelor.apps.hr.db.Absence; import com.axelor.apps.hr.db.Employee; import com.axelor.apps.hr.db.DailyReport; +import com.axelor.apps.hr.db.MonthlyReport; import com.google.inject.persist.Transactional; import java.time.LocalDate; import java.util.List; @@ -11,13 +12,9 @@ import java.util.List; public interface MonthlyReportService { @Transactional - void createMensuelReport( - Employee employee, - Period period, - LocalDate startDate, - LocalDate endDate, - List employeeDailyReports, - List employeeAbsences); + void createMensuelReport(Employee employee, Period period, LocalDate startDate, LocalDate endDate, List employeeDailyReports, List employeeAbsences); + + void updateMensuelReport(MonthlyReport monthlyReport, Employee employee, Period period, LocalDate startDate, LocalDate endDate, List employeeDailyReports, List employeeAbsences); /*void calculePrimeAssiduite(Mensuels mensuels);*/ } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportServiceImpl.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportServiceImpl.java index 8b2fdcc..a3ea988 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportServiceImpl.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportServiceImpl.java @@ -16,6 +16,7 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; import java.util.List; +import java.util.Set; import javax.inject.Inject; import com.axelor.inject.Beans; @@ -41,6 +42,7 @@ public class MonthlyReportServiceImpl implements MonthlyReportService { @Transactional @Override + public void createMensuelReport(Employee employee, Period period, LocalDate startDate, LocalDate endDate, List employeeDailyReports, List employeeAbsences) { Boolean hasNuissance = employee.getHasNuissance(); @@ -97,19 +99,31 @@ public class MonthlyReportServiceImpl implements MonthlyReportService { monthlyNightHours = monthlyNightHours.add(dailyReport.getNightHours()); totalWorkHours = totalWorkHours.add(dailyReport.getWorkHours()); - BigDecimal heureSup50 = dailyReport.getExtraHours50(); - BigDecimal heureSup100 = dailyReport.getExtraHours100(); - BigDecimal itp = dailyReport.getItp(); - BigDecimal nuissance = dailyReport.getNuissance(); + BigDecimal heureSup50 = dailyReport.getExtraHours50() != null ? dailyReport.getExtraHours50() : BigDecimal.ZERO; + BigDecimal heureSup100 = dailyReport.getExtraHours100() != null ? dailyReport.getExtraHours100() : BigDecimal.ZERO; + BigDecimal itp = dailyReport.getItp() != null ? dailyReport.getItp() : BigDecimal.ZERO; + BigDecimal nuissance = dailyReport.getNuissance() != null ? dailyReport.getNuissance() : BigDecimal.ZERO; - if(dailyReport.getDeduceSupHours50()) - heureSup50 = heureSup50.subtract(dailyReport.getSupHours50ToDeduce()); - if(dailyReport.getDeduceSupHours100()) - heureSup100 = heureSup100.subtract(dailyReport.getSupHours100ToDeduce()); - if(dailyReport.getDeduceItp()) - itp = itp.subtract(dailyReport.getItpToDeduce()); - if(dailyReport.getDeduceNuissance()) - nuissance = nuissance.subtract(dailyReport.getNuissanceToDeduce()); + BigDecimal supHours50ToDeduce = dailyReport.getSupHours50ToDeduce() != null ? dailyReport.getSupHours50ToDeduce() : BigDecimal.ZERO; + BigDecimal supHours100ToDeduce = dailyReport.getSupHours100ToDeduce() != null ? dailyReport.getSupHours100ToDeduce() : BigDecimal.ZERO; + BigDecimal itpToDeduce = dailyReport.getItpToDeduce() != null ? dailyReport.getItpToDeduce() : BigDecimal.ZERO; + BigDecimal nuissanceToDeduce = dailyReport.getNuissanceToDeduce() != null ? dailyReport.getNuissanceToDeduce() : BigDecimal.ZERO; + + if (dailyReport.getDeduceSupHours50() && heureSup50.compareTo(supHours50ToDeduce) > 0) { + heureSup50 = heureSup50.subtract(supHours50ToDeduce); + } + + if (dailyReport.getDeduceSupHours100() && heureSup100.compareTo(supHours100ToDeduce) > 0) { + heureSup100 = heureSup100.subtract(supHours100ToDeduce); + } + + if (dailyReport.getDeduceItp() && itp.compareTo(itpToDeduce) > 0) { + itp = itp.subtract(itpToDeduce); + } + + if (dailyReport.getDeduceNuissance() && nuissance.compareTo(nuissanceToDeduce) > 0) { + nuissance = nuissance.subtract(nuissanceToDeduce); + } monthlyITP = monthlyITP.add(itp); totalNuissance = totalNuissance.add(nuissance); @@ -158,83 +172,111 @@ public class MonthlyReportServiceImpl implements MonthlyReportService { } Boolean isAuthorizedAbsence = dailyReport.getIsAuthorizedAbsence(); + Boolean isAuthorizedLateArrival = dailyReport.getIsAuthorizedLateArrival(); + Boolean isAuthorizedEarlyDeparture = dailyReport.getIsAuthorizedEarlyDeparture(); LeaveRequest leaveRequest = dailyReport.getLeaveRequest(); - Absence absence = dailyReport.getAbsence(); + Set absences = dailyReport.getAbsenceSet(); - if (isAuthorizedAbsence == false && leaveRequest == null && absence == null) { - irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getAbsenceHours()); - } else if(isAuthorizedAbsence){ - justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getAbsenceHours()); - } else if(leaveRequest != null){ - recuperationLeaveAbsenceMonth = recuperationLeaveAbsenceMonth.add(dailyReport.getAbsenceHours()); - } else if(absence != null){ - totalAbsence = dailyReport.getAbsenceHours(); - switch (absence.getAbsenceType()) { - case "Absence autorisé de payée": - breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence); - break; - case "Absence allaitement": - breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence); - break; - case "Absence congé récupération": - recuperationLeaveAbsenceMonth = recuperationLeaveAbsenceMonth.add(totalAbsence); - break; - case "Absence formation": - trainingAbsenceMonth = trainingAbsenceMonth.add(totalAbsence); - break; - case "Absence mission": - missionAbsenceMonth = missionAbsenceMonth.add(totalAbsence); - break; - case "Absence hospitalisation": - hospitalizationAbsenceMonth = hospitalizationAbsenceMonth.add(totalAbsence); - break; - case "Absence maladie": - sicknessAbsenceMonth = sicknessAbsenceMonth.add(totalAbsence); - break; - case "Accident de travail": - workAccidentMonth = workAccidentMonth.add(totalAbsence); - break; - case "Maternité": - maternityMonth = maternityMonth.add(totalAbsence); - break; - case "Absence autorisée non payée": - authorizedUnpaidAbsenceMonth = authorizedUnpaidAbsenceMonth.add(totalAbsence); - break; - case "Absence conge annuel": - annualLeaveAbsenceMonth = annualLeaveAbsenceMonth.add(totalAbsence); - break; - case "Absence conge sans solde": - unpaidLeaveAbsenceMonth = unpaidLeaveAbsenceMonth.add(totalAbsence); - break; - case "Absence conge statutaire": - statutoryLeaveAbsenceMonth = statutoryLeaveAbsenceMonth.add(totalAbsence); - break; - case "Absence démission": - resignationAbsenceMonth = resignationAbsenceMonth.add(totalAbsence); - break; - case "Absence fin de contrat": - contractEndAbsenceMonth = contractEndAbsenceMonth.add(totalAbsence); - break; - case "Absence justifieé": - justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence); - break; - case "Absence recrutement": - recruitmentAbsenceMonth = recruitmentAbsenceMonth.add(totalAbsence); - break; - case "Suspension": - suspensionAbsenceMonth = suspensionAbsenceMonth.add(totalAbsence); - break; - case "Absence irrégulière": - irregularAbsenceMonth = irregularAbsenceMonth.add(totalAbsence); - break; - case "Absence service militaire": - militaryServiceAbsence = militaryServiceAbsence.add(totalAbsence); - break; - default: - // Handle default case - break; + if (dailyReport.getAbsenceHours() != null && dailyReport.getAbsenceHours().compareTo(BigDecimal.ZERO) > 0) { + if (isAuthorizedAbsence == false && isAuthorizedLateArrival == false && isAuthorizedEarlyDeparture == false && leaveRequest == null && absences == null) { + irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getAbsenceHours()); + } else if(isAuthorizedAbsence){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getAbsenceHours()); + } else if(isAuthorizedLateArrival){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getLateArrival()); + } else if(isAuthorizedEarlyDeparture){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getEarlyDeparture()); + } else if(leaveRequest != null){ + recuperationLeaveAbsenceMonth = recuperationLeaveAbsenceMonth.add(dailyReport.getAbsenceHours()); + } else if(absences != null){ + for(Absence absence:absences){ + totalAbsence = dailyReport.getAbsenceHours(); + switch (absence.getAbsenceType()) { + case 0: + breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence); + break; + case 1: + recuperationLeaveAbsenceMonth = recuperationLeaveAbsenceMonth.add(totalAbsence); + break; + case 2: + breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence); + break; + case 3: + trainingAbsenceMonth = trainingAbsenceMonth.add(totalAbsence); + break; + case 4: + missionAbsenceMonth = missionAbsenceMonth.add(totalAbsence); + break; + case 5: + hospitalizationAbsenceMonth = hospitalizationAbsenceMonth.add(totalAbsence); + break; + case 6: + sicknessAbsenceMonth = sicknessAbsenceMonth.add(totalAbsence); + break; + case 7: + workAccidentMonth = workAccidentMonth.add(totalAbsence); + break; + case 8: + maternityMonth = maternityMonth.add(totalAbsence); + break; + case 9: + authorizedUnpaidAbsenceMonth = authorizedUnpaidAbsenceMonth.add(totalAbsence); + break; + case 10: + annualLeaveAbsenceMonth = annualLeaveAbsenceMonth.add(totalAbsence); + break; + case 11: + unpaidLeaveAbsenceMonth = unpaidLeaveAbsenceMonth.add(totalAbsence); + break; + case 12: + statutoryLeaveAbsenceMonth = statutoryLeaveAbsenceMonth.add(totalAbsence); + break; + case 13: + resignationAbsenceMonth = resignationAbsenceMonth.add(totalAbsence); + break; + case 14: + contractEndAbsenceMonth = contractEndAbsenceMonth.add(totalAbsence); + break; + case 15: + justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence); + break; + case 16: + recruitmentAbsenceMonth = recruitmentAbsenceMonth.add(totalAbsence); + break; + case 17: + suspensionAbsenceMonth = suspensionAbsenceMonth.add(totalAbsence); + break; + case 18: + militaryServiceAbsence = militaryServiceAbsence.add(totalAbsence); + break; + case 19: + if(dailyReport.getIsAuthorizedAbsence()){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence); + } else { + irregularAbsenceMonth = irregularAbsenceMonth.add(totalAbsence); + } + break; + case 20: + if(dailyReport.getIsAuthorizedLateArrival()){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getLateArrival()); + } else { + irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getLateArrival()); + } + break; + case 21: + if(dailyReport.getIsAuthorizedEarlyDeparture()){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getEarlyDeparture()); + } else { + irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getEarlyDeparture()); + } + break; + default: + // Handle default case + break; + } + } } - } + } } // Update or create MonthlyReport instance with calculated values @@ -278,6 +320,284 @@ public class MonthlyReportServiceImpl implements MonthlyReportService { // Save the MonthlyReport instance monthlyReportRepository.save(monthlyReport); } + + public void updateMensuelReport(MonthlyReport monthlyReport, Employee employee, Period period, LocalDate startDate, LocalDate endDate, List employeeDailyReports, List employeeAbsences) { + + Boolean hasNuissance = employee.getHasNuissance(); + LocalDate firstDayOfMonth = endDate.with(TemporalAdjusters.firstDayOfMonth()); + // Perform calculations + Integer monthlyAllowance = 0; + Integer monthlyAllowanceRecall = 0; + BigDecimal monthlyNightHours = BigDecimal.ZERO; + BigDecimal monthlyExtraHours50 = BigDecimal.ZERO; + BigDecimal monthlyExtraHours100 = BigDecimal.ZERO; + BigDecimal excessHours50 = BigDecimal.ZERO; + BigDecimal excessHours100 = BigDecimal.ZERO; + BigDecimal totalSupHours = BigDecimal.ZERO; + + BigDecimal monthlyITP = BigDecimal.ZERO; + BigDecimal totalNuissance = BigDecimal.ZERO; + BigDecimal totalAbsence = BigDecimal.ZERO; + BigDecimal totalWorkHours = BigDecimal.ZERO; + + BigDecimal breastfeedingAbsenceMonth = BigDecimal.ZERO; + BigDecimal authorizedPaidAbsenceMonth = BigDecimal.ZERO; + BigDecimal recuperationLeaveAbsenceMonth = BigDecimal.ZERO; + BigDecimal trainingAbsenceMonth = BigDecimal.ZERO; + BigDecimal missionAbsenceMonth = BigDecimal.ZERO; + BigDecimal hospitalizationAbsenceMonth = BigDecimal.ZERO; + BigDecimal sicknessAbsenceMonth = BigDecimal.ZERO; + BigDecimal workAccidentMonth = BigDecimal.ZERO; + BigDecimal maternityMonth = BigDecimal.ZERO; + BigDecimal authorizedUnpaidAbsenceMonth = BigDecimal.ZERO; + BigDecimal annualLeaveAbsenceMonth = BigDecimal.ZERO; + BigDecimal unpaidLeaveAbsenceMonth = BigDecimal.ZERO; + BigDecimal statutoryLeaveAbsenceMonth = BigDecimal.ZERO; + BigDecimal resignationAbsenceMonth = BigDecimal.ZERO; + BigDecimal contractEndAbsenceMonth = BigDecimal.ZERO; + BigDecimal justifiedAbsenceMonth = BigDecimal.ZERO; + BigDecimal recruitmentAbsenceMonth = BigDecimal.ZERO; + BigDecimal suspensionAbsenceMonth = BigDecimal.ZERO; + BigDecimal militaryServiceAbsence = BigDecimal.ZERO; + BigDecimal irregularAbsenceMonth = BigDecimal.ZERO; + + //monthlyAllowance = monthlyAllowance + remainingDateToLastOfMonth; + + // Calculate totals for DailyReport + for (DailyReport dailyReport : employeeDailyReports) { + // Panier + /*LocalDate reportDate = dailyReport.getReportDate(); + Integer panier = dailyReport.getPanier(); + if (reportDate.isAfter(firstDayOfMonth) && panier == 1) { + monthlyAllowance = monthlyAllowance + 1; + }*/ + + monthlyAllowance = monthlyAllowance + dailyReport.getAllowance();; + monthlyAllowanceRecall = monthlyAllowanceRecall + dailyReport.getAllowanceRecall(); + monthlyNightHours = monthlyNightHours.add(dailyReport.getNightHours()); + totalWorkHours = totalWorkHours.add(dailyReport.getWorkHours()); + + BigDecimal heureSup50 = dailyReport.getExtraHours50() != null ? dailyReport.getExtraHours50() : BigDecimal.ZERO; + BigDecimal heureSup100 = dailyReport.getExtraHours100() != null ? dailyReport.getExtraHours100() : BigDecimal.ZERO; + BigDecimal itp = dailyReport.getItp() != null ? dailyReport.getItp() : BigDecimal.ZERO; + BigDecimal nuissance = dailyReport.getNuissance() != null ? dailyReport.getNuissance() : BigDecimal.ZERO; + + BigDecimal supHours50ToDeduce = dailyReport.getSupHours50ToDeduce() != null ? dailyReport.getSupHours50ToDeduce() : BigDecimal.ZERO; + BigDecimal supHours100ToDeduce = dailyReport.getSupHours100ToDeduce() != null ? dailyReport.getSupHours100ToDeduce() : BigDecimal.ZERO; + BigDecimal itpToDeduce = dailyReport.getItpToDeduce() != null ? dailyReport.getItpToDeduce() : BigDecimal.ZERO; + BigDecimal nuissanceToDeduce = dailyReport.getNuissanceToDeduce() != null ? dailyReport.getNuissanceToDeduce() : BigDecimal.ZERO; + + if (dailyReport.getDeduceSupHours50() && heureSup50.compareTo(supHours50ToDeduce) > 0) { + heureSup50 = heureSup50.subtract(supHours50ToDeduce); + } + + if (dailyReport.getDeduceSupHours100() && heureSup100.compareTo(supHours100ToDeduce) > 0) { + heureSup100 = heureSup100.subtract(supHours100ToDeduce); + } + + if (dailyReport.getDeduceItp() && itp.compareTo(itpToDeduce) > 0) { + itp = itp.subtract(itpToDeduce); + } + + if (dailyReport.getDeduceNuissance() && nuissance.compareTo(nuissanceToDeduce) > 0) { + nuissance = nuissance.subtract(nuissanceToDeduce); + } + + monthlyITP = monthlyITP.add(itp); + totalNuissance = totalNuissance.add(nuissance); + + // Sup Hours + if (dailyReport.getIsValidSupHours()) { + + // Handle HeureSup50 + if (heureSup50 != null) { + if (totalSupHours.add(heureSup50).compareTo(new BigDecimal(32)) <= 0) { + // If the total hours are still less than or equal to 32 after adding new hours + monthlyExtraHours50 = monthlyExtraHours50.add(heureSup50); + totalSupHours = totalSupHours.add(heureSup50); + } else if (totalSupHours.compareTo(new BigDecimal(32)) < 0) { + // If totalSupHours is less than 32, but adding heureSup50 exceeds 32 + BigDecimal remainingHours = new BigDecimal(32).subtract(totalSupHours); + monthlyExtraHours50 = monthlyExtraHours50.add(remainingHours); + totalSupHours = totalSupHours.add(remainingHours); + + // Add the excess to excessHours50 + excessHours50 = excessHours50.add(heureSup50.subtract(remainingHours)); + } else { + // If totalSupHours already exceeds 32, all new hours go to excess + excessHours50 = excessHours50.add(heureSup50); + } + } + // Handle HeureSup100 + if (heureSup100 != null) { + if (totalSupHours.add(heureSup100).compareTo(new BigDecimal(32)) <= 0) { + // If the total hours are still less than or equal to 32 after adding new hours + monthlyExtraHours100 = monthlyExtraHours100.add(heureSup100); + totalSupHours = totalSupHours.add(heureSup100); + } else if (totalSupHours.compareTo(new BigDecimal(32)) < 0) { + // If totalSupHours is less than 32, but adding heureSup100 exceeds 32 + BigDecimal remainingHours = new BigDecimal(32).subtract(totalSupHours); + monthlyExtraHours100 = monthlyExtraHours100.add(remainingHours); + totalSupHours = totalSupHours.add(remainingHours); + + // Add the excess to excessHours100 + excessHours100 = excessHours100.add(heureSup100.subtract(remainingHours)); + } else { + // If totalSupHours already exceeds 32, all new hours go to excess + excessHours100 = excessHours100.add(heureSup100); + } + } + } + + Boolean isAuthorizedAbsence = dailyReport.getIsAuthorizedAbsence(); + Boolean isAuthorizedLateArrival = dailyReport.getIsAuthorizedLateArrival(); + Boolean isAuthorizedEarlyDeparture = dailyReport.getIsAuthorizedEarlyDeparture(); + LeaveRequest leaveRequest = dailyReport.getLeaveRequest(); + Set absences = dailyReport.getAbsenceSet(); + + if (dailyReport.getAbsenceHours() != null && dailyReport.getAbsenceHours().compareTo(BigDecimal.ZERO) > 0) { + if (isAuthorizedAbsence == false && isAuthorizedLateArrival == false && isAuthorizedEarlyDeparture == false && leaveRequest == null && absences == null) { + irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getAbsenceHours()); + } else if(isAuthorizedAbsence){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getAbsenceHours()); + } else if(isAuthorizedLateArrival){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getLateArrival()); + } else if(isAuthorizedEarlyDeparture){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getEarlyDeparture()); + } else if(leaveRequest != null){ + recuperationLeaveAbsenceMonth = recuperationLeaveAbsenceMonth.add(dailyReport.getAbsenceHours()); + } else if(absences != null){ + for(Absence absence:absences){ + totalAbsence = dailyReport.getAbsenceHours(); + switch (absence.getAbsenceType()) { + case 0: + breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence); + break; + case 1: + recuperationLeaveAbsenceMonth = recuperationLeaveAbsenceMonth.add(totalAbsence); + break; + case 2: + breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence); + break; + case 3: + trainingAbsenceMonth = trainingAbsenceMonth.add(totalAbsence); + break; + case 4: + missionAbsenceMonth = missionAbsenceMonth.add(totalAbsence); + break; + case 5: + hospitalizationAbsenceMonth = hospitalizationAbsenceMonth.add(totalAbsence); + break; + case 6: + sicknessAbsenceMonth = sicknessAbsenceMonth.add(totalAbsence); + break; + case 7: + workAccidentMonth = workAccidentMonth.add(totalAbsence); + break; + case 8: + maternityMonth = maternityMonth.add(totalAbsence); + break; + case 9: + authorizedUnpaidAbsenceMonth = authorizedUnpaidAbsenceMonth.add(totalAbsence); + break; + case 10: + annualLeaveAbsenceMonth = annualLeaveAbsenceMonth.add(totalAbsence); + break; + case 11: + unpaidLeaveAbsenceMonth = unpaidLeaveAbsenceMonth.add(totalAbsence); + break; + case 12: + statutoryLeaveAbsenceMonth = statutoryLeaveAbsenceMonth.add(totalAbsence); + break; + case 13: + resignationAbsenceMonth = resignationAbsenceMonth.add(totalAbsence); + break; + case 14: + contractEndAbsenceMonth = contractEndAbsenceMonth.add(totalAbsence); + break; + case 15: + justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence); + break; + case 16: + recruitmentAbsenceMonth = recruitmentAbsenceMonth.add(totalAbsence); + break; + case 17: + suspensionAbsenceMonth = suspensionAbsenceMonth.add(totalAbsence); + break; + case 18: + militaryServiceAbsence = militaryServiceAbsence.add(totalAbsence); + break; + case 19: + if(dailyReport.getIsAuthorizedAbsence()){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence); + } else { + irregularAbsenceMonth = irregularAbsenceMonth.add(totalAbsence); + } + break; + case 20: + if(dailyReport.getIsAuthorizedLateArrival()){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getLateArrival()); + } else { + irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getLateArrival()); + } + break; + case 21: + if(dailyReport.getIsAuthorizedEarlyDeparture()){ + justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getEarlyDeparture()); + } else { + irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getEarlyDeparture()); + } + break; + default: + // Handle default case + break; + } + } + } + } + } + + // Update or create MonthlyReport instance with calculated values + monthlyReport.setPeriod(period); + monthlyReport.setEmployee(employee); + monthlyReport.setMonthlyAllowance(monthlyAllowance); + monthlyReport.setMonthlyAllowanceRecall(monthlyAllowanceRecall); + monthlyReport.setMonthlyNightHours(monthlyNightHours); + monthlyReport.setMonthlyExtraHours50(monthlyExtraHours50); + monthlyReport.setMonthlyExtraHours100(monthlyExtraHours100); + monthlyReport.setExcessHours50(excessHours50); + monthlyReport.setExcessHours100(excessHours100); + + monthlyReport.setMonthlyITP(monthlyITP); + monthlyReport.setTotalNuissance(totalNuissance); + monthlyReport.setTotalWorkHours(totalWorkHours); + + // Set total absence values for each specific type + monthlyReport.setBreastfeedingAbsenceMonth(breastfeedingAbsenceMonth); + monthlyReport.setAuthorizedPaidAbsenceMonth(authorizedPaidAbsenceMonth); + monthlyReport.setAuthorizedUnpaidAbsenceMonth(authorizedUnpaidAbsenceMonth); + monthlyReport.setAnnualLeaveAbsenceMonth(annualLeaveAbsenceMonth); + monthlyReport.setRecuperationLeaveAbsenceMonth(recuperationLeaveAbsenceMonth); + monthlyReport.setUnpaidLeaveAbsenceMonth(unpaidLeaveAbsenceMonth); + monthlyReport.setStatutoryLeaveAbsenceMonth(statutoryLeaveAbsenceMonth); + monthlyReport.setResignationAbsenceMonth(resignationAbsenceMonth); + monthlyReport.setContractEndAbsenceMonth(contractEndAbsenceMonth); + monthlyReport.setTrainingAbsenceMonth(trainingAbsenceMonth); + monthlyReport.setHospitalizationAbsenceMonth(hospitalizationAbsenceMonth); + monthlyReport.setMissionAbsenceMonth(missionAbsenceMonth); + monthlyReport.setSicknessAbsenceMonth(sicknessAbsenceMonth); + monthlyReport.setWorkAccidentMonth(workAccidentMonth); + monthlyReport.setMaternityMonth(maternityMonth); + monthlyReport.setJustifiedAbsenceMonth(justifiedAbsenceMonth); + monthlyReport.setRecruitmentAbsenceMonth(recruitmentAbsenceMonth); + monthlyReport.setSuspensionAbsenceMonth(suspensionAbsenceMonth); + monthlyReport.setMilitaryServiceAbsence(militaryServiceAbsence); + monthlyReport.setIrregularAbsenceMonth(irregularAbsenceMonth); + + // Save the MonthlyReport instance + monthlyReportRepository.save(monthlyReport); + } + /* @Transactional public void calculePrimeAssiduite(MonthlyReport monthlyReport) { diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/employee/EmployeeService.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/employee/EmployeeService.java index db5e560..a25d196 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/employee/EmployeeService.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/employee/EmployeeService.java @@ -25,6 +25,7 @@ import com.google.inject.persist.Transactional; import java.math.BigDecimal; import java.time.LocalDate; import java.util.Map; +import java.util.List; public interface EmployeeService extends UserService { @@ -43,4 +44,14 @@ public interface EmployeeService extends UserService { /** Generates a new {@link DPAE} for given {@link Employee} and returns its id. */ @Transactional(rollbackOn = {Exception.class}) Long generateNewDPAE(Employee employee) throws AxelorException; + + @Transactional + public void setEmployeeEnrolled(Employee employee); + + @Transactional(rollbackOn = {Exception.class}) + public void updateEmployeeConfig(Employee employee, Integer configSelect, Boolean status) throws AxelorException ; + + @Transactional(rollbackOn = {Exception.class}) + public void massUpdateEmployeeConfig(List employeesIds, Integer configSelect, Boolean status) throws AxelorException; + } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/employee/EmployeeServiceImpl.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/employee/EmployeeServiceImpl.java index 9b897a2..8ba166b 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/employee/EmployeeServiceImpl.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/employee/EmployeeServiceImpl.java @@ -250,4 +250,47 @@ public class EmployeeServiceImpl extends UserServiceImpl implements EmployeeServ Beans.get(EmployeeRepository.class).save(employee); return newDPAE.getId(); } + + @Override + @Transactional + public void setEmployeeEnrolled(Employee employee){ + employee.setIsEnrolled(true); + Beans.get(EmployeeRepository.class).save(employee); + } + + @Override + @Transactional(rollbackOn = {Exception.class}) + public void updateEmployeeConfig(Employee employee, Integer configSelect, Boolean status) + throws AxelorException { + switch (configSelect) { + case 1: // ITP + employee.setHasItp(status); + break; + case 2: // Nuissance + employee.setHasNuissance(status); + break; + case 3: // Transfaire + employee.setIsTransfaire(status); + break; + default: + return; // Invalid configSelect, stop processing + } + Beans.get(EmployeeRepository.class).save(employee); + } + + @Override + @Transactional(rollbackOn = {Exception.class}) + public void massUpdateEmployeeConfig(List employeesIds, Integer configSelect, Boolean status) throws AxelorException{ + List employees = + Beans.get(EmployeeRepository.class) + .all() + .filter("self.id in (?1)", employeesIds) + .fetch(); + + if (employeesIds != null || !employeesIds.isEmpty()) { + for (Employee employee : employees) { + this.updateEmployeeConfig(employee, configSelect, status); + } + } + } } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AbsenceController.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AbsenceController.java index e45f96e..011b75f 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AbsenceController.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AbsenceController.java @@ -15,6 +15,10 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.List; +import com.axelor.i18n.I18n; +import com.axelor.meta.schema.actions.ActionView; +import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder; +import java.util.stream.Collectors; public class AbsenceController { @@ -27,22 +31,43 @@ public class AbsenceController { LocalDate absenceEndDate = absence.getEndDate().toLocalDate(); Employee employee = absence.getEmployee(); - List dailyreports = - Beans.get(DailyReportRepository.class) - .all() - .filter( - "self.employee = :employee and self.reportDate between :absenceStartDate and :absenceEndDate") - .bind("employee", employee) - .bind("absenceStartDate", absenceStartDate) - .bind("absenceEndDate", absenceEndDate) - .fetch(); + // Fetch all existing daily reports associated with this absence + List existingReports = + Beans.get(DailyReportRepository.class) + .all() + .filter("self.absenceSet = :absence") + .bind("absence", absence) + .fetch(); + + if (existingReports != null && !existingReports.isEmpty()) { + // Detach absence only from reports that are outside the new date range + List reportsToDetach = existingReports.stream() + .filter(report -> report.getReportDate().isBefore(absenceStartDate) || report.getReportDate().isAfter(absenceEndDate)) + .collect(Collectors.toList()); + + // Detach absence from these specific reports + if (!reportsToDetach.isEmpty()) { + Beans.get(AbsenceServiceImpl.class).deAttachTheAbsenceWithDailyReport(absence, reportsToDetach, false); + } + } + + List newReports = + Beans.get(DailyReportRepository.class) + .all() + .filter( + "self.employee = :employee and self.reportDate between :absenceStartDate and :absenceEndDate") + .bind("employee", employee) + .bind("absenceStartDate", absenceStartDate) + .bind("absenceEndDate", absenceEndDate) + .fetch(); + // Check if there are any reports - if (dailyreports.isEmpty()) { + if (newReports.isEmpty()) { return; } - Beans.get(AbsenceServiceImpl.class).attachTheAbsenceWithDailyReport(absence, dailyreports); + Beans.get(AbsenceServiceImpl.class).attachTheAbsenceWithDailyReport(absence, newReports); response.setReload(true); } catch (Exception e) { e.printStackTrace(); @@ -52,7 +77,7 @@ public class AbsenceController { public void chooseAbsenceType(ActionRequest request, ActionResponse response) { try { // Get the selected absence type from the request parameters - String selectedType = (String) request.getContext().get("selectedType"); + Integer selectedType = (Integer) request.getContext().get("selectedType"); // Get the list of selected absence IDs List absenceIds = (List) request.getContext().get("_ids"); @@ -89,7 +114,14 @@ public class AbsenceController { .bind("absence", absenceId) .fetch(); - Beans.get(AbsenceServiceImpl.class).deAttachTheAbsenceWithDailyReport(absence, dailyreports); + Beans.get(AbsenceServiceImpl.class).deAttachTheAbsenceWithDailyReport(absence, dailyreports, true); + ActionViewBuilder actionView = + ActionView.define(I18n.get("Absences")) + .model(Absence.class.getName()) + .add("grid", "absence-grid") + .add("form", "absence-form"); + + response.setView(actionView.map()); } catch (Exception e) { e.printStackTrace(); } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AuthorizationController.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AuthorizationController.java index e4688a1..eb734bd 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AuthorizationController.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AuthorizationController.java @@ -137,6 +137,42 @@ public class AuthorizationController { } } + public void fetchSalaryAuthorization(ActionRequest request, ActionResponse response) { + try { + String accessToken = AppBaseController.getAccessToken(); + if (accessToken == null) { + logger.error("Access token is null, unable to proceed."); + return; + } + String jsonResponse = fetchAP(accessToken); + + if (jsonResponse != null) { + try { + JSONArray jsonArray = new JSONArray(jsonResponse); + + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + Beans.get(AuthorizationService.class).saveAP(jsonObject); + } + + // Reload the response after successfully saving extra hours + response.setReload(true); + + } catch (JSONException e) { + // Log the specific JSON parsing error + System.err.println("Failed to parse JSON: " + jsonResponse); + e.printStackTrace(); + } + } else { + System.err.println("No response received from fetchAP."); + } + } catch (Exception e) { + // General catch for unexpected exceptions + System.err.println("An error occurred while fetching Salary Authorization: " + e.getMessage()); + e.printStackTrace(); + } + } + public String fetchAA(String accessToken) { String endpoint = BASE_URL + "tickets/aa/"; HttpURLConnection conn = null; @@ -283,4 +319,55 @@ public class AuthorizationController { } } } + + // Authorisation de paye + public String fetchAP(String accessToken) { + String endpoint = BASE_URL + "tickets/ap/"; + HttpURLConnection conn = null; + + try { + URL url = new URL(endpoint); + conn = (HttpURLConnection) url.openConnection(); + + conn.setRequestMethod("GET"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Accept", "application/json"); + // Include the access token in the Authorization header + conn.setRequestProperty("Authorization", "Bearer " + accessToken); + + int responseCode = conn.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + try (BufferedReader br = + new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) { + StringBuilder response = new StringBuilder(); + String responseLine; + while ((responseLine = br.readLine()) != null) { + response.append(responseLine.trim()); + } + // Process the response as needed + return response.toString(); + } + } else { + // Read the error stream for more details + StringBuilder errorResponse = new StringBuilder(); + try (BufferedReader br = + new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"))) { + String errorLine; + while ((errorLine = br.readLine()) != null) { + errorResponse.append(errorLine.trim()); + } + } + throw new RuntimeException( + "Failed: HTTP error code: " + responseCode + ", Error: " + errorResponse.toString()); + } + } catch (Exception e) { + e.printStackTrace(); + return null; // Return null in case of failure + } finally { + if (conn != null) { + conn.disconnect(); // Ensure connection is closed + } + } + } + } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/DailyReportController.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/DailyReportController.java index 4ed42cb..5908ca4 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/DailyReportController.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/DailyReportController.java @@ -1,19 +1,28 @@ package com.axelor.apps.hr.web; import com.axelor.apps.hr.db.Shift; +import com.axelor.apps.hr.db.Employee; import com.axelor.apps.hr.db.DailyReport; import com.axelor.apps.hr.db.repo.DailyReportRepository; import com.axelor.apps.hr.db.repo.ShiftRepository; +import com.axelor.apps.hr.db.repo.EmployeeRepository; +import com.axelor.apps.hr.service.DailyReportService; import com.axelor.apps.hr.service.DailyReportServiceImpl; import com.axelor.inject.Beans; import com.axelor.rpc.ActionRequest; import com.axelor.rpc.ActionResponse; import java.lang.invoke.MethodHandles; +import java.util.LinkedHashMap; +import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; import java.time.LocalTime; +import java.time.LocalDate; +import java.math.BigDecimal; +import com.axelor.exception.service.TraceBackService; public class DailyReportController { @@ -115,4 +124,112 @@ public class DailyReportController { response.setReload(true); response.setFlash("Working hours calculated successfully."); } + + public void massDeducePrime(ActionRequest request, ActionResponse response) { + + Integer primeSelection = (Integer) request.getContext().get("primesSelect"); + String startDateStr = (String) request.getContext().get("startDate"); + String endDateStr = (String) request.getContext().get("endDate"); + + LocalDate startDate = null; + LocalDate endDate = null; + + try { + if (startDateStr != null) { + startDate = LocalDate.parse(startDateStr); + } + if (endDateStr != null) { + endDate = LocalDate.parse(endDateStr); + } + + // Validate the dates + if (startDate == null || endDate == null) { + response.setFlash("Start date or end date is missing or invalid."); + return; + } + + if (startDate.isAfter(endDate)) { + response.setFlash("Start date cannot be after end date."); + return; + } + + } catch (DateTimeParseException e) { + response.setFlash("Invalid date format for startDate or endDate. Expected format: yyyy-MM-dd."); + return; + } + + Object valueToDeduceObj = request.getContext().get("valueToDeduce"); + + BigDecimal valueToDeduce = null; + if (valueToDeduceObj instanceof Integer) { + valueToDeduce = BigDecimal.valueOf((Integer) valueToDeduceObj); + } else if (valueToDeduceObj instanceof BigDecimal) { + valueToDeduce = (BigDecimal) valueToDeduceObj; + } else if (valueToDeduceObj instanceof String) { + try { + valueToDeduce = new BigDecimal((String) valueToDeduceObj); + } catch (NumberFormatException e) { + response.setFlash("Value to deduce must be a valid number."); + return; + } + } else { + response.setFlash("Invalid value to deduce: unsupported type " + (valueToDeduceObj != null ? valueToDeduceObj.getClass().getName() : "null")); + return; + } + + Object employeesObject = request.getContext().get("employees"); + + // Initialize the employees list + List employees = new ArrayList<>(); + + if (employeesObject instanceof List) { + List> employeesList = (List>) employeesObject; + + for (LinkedHashMap employeeMap : employeesList) { + Integer employeeIdInt = (Integer) employeeMap.get("id"); + + if (employeeIdInt != null) { + Long employeeId = employeeIdInt.longValue(); + Employee employee = Beans.get(EmployeeRepository.class).find(employeeId); + + if (employee != null) { + employees.add(employee); + } + } + } + } + + if (employees.isEmpty()) { + response.setFlash("No employees selected."); + return; + } + + List employeeIds = new ArrayList<>(); + for (Employee employee : employees) { + employeeIds.add(employee.getId().intValue()); + } + + // Fetch all rapport journaliers within the date range for all employees + List dailyReportList = Beans.get(DailyReportRepository.class) + .all() + .filter("self.employee.id in :employeeIds and self.reportDate >= :startDate and self.reportDate <= :endDate") + .bind("startDate", startDate) + .bind("endDate", endDate) + .bind("employeeIds", employeeIds) + .fetch(); + + try { + if (!dailyReportList.isEmpty()) { + Beans.get(DailyReportService.class).massDeducePrimes(dailyReportList, primeSelection, valueToDeduce); + response.setReload(true); + response.setFlash("Prime deductions processed successfully."); + } else { + response.setFlash("No reports found for the selected date range."); + } + } catch (Exception e) { + TraceBackService.trace(response, e); + response.setFlash("An error occurred while processing the request."); + } + } + } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/EmployeeController.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/EmployeeController.java index 3346388..ec21728 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/EmployeeController.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/EmployeeController.java @@ -40,6 +40,7 @@ import com.axelor.rpc.ActionResponse; import com.google.inject.Singleton; import java.lang.invoke.MethodHandles; import java.util.Map; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import wslite.json.JSONException; @@ -47,11 +48,13 @@ import wslite.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import com.axelor.app.AppSettings; @Singleton public class EmployeeController { private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private final String pythonScriptDir = AppSettings.get().get("services.dir"); public void showAnnualReport(ActionRequest request, ActionResponse response) throws JSONException, NumberFormatException, AxelorException { @@ -164,12 +167,18 @@ public class EmployeeController { if (employee.getContactPartner() != null){ String employeeRegistrationNumber = employee.getRegistrationNumber(); String employeeName = employee.getContactPartner().getName(); + + if (pythonScriptDir == null || pythonScriptDir.isEmpty()) { + LOG.error("Pythons script path is not configured in AppSettings."); + return; + } try { + String[] args = { "python", - "C:\\Users\\administrator\\Desktop\\attendance\\main.py", + pythonScriptDir + "\\Attendance\\main.py", "--commande", "create", "--ip_address", ipAdress, "--code", code, @@ -198,6 +207,7 @@ public class EmployeeController { // Check if the process ran successfully if (exitCode == 0) { LOG.info("Python script executed successfully (Employee Enrolling)."); + Beans.get(EmployeeService.class).setEmployeeEnrolled(employee); response.setFlash("Employee enrolled successfully."); } else { LOG.error("Python script execution (Employee Enrolling) failed with exit code: " + exitCode); @@ -223,4 +233,47 @@ public class EmployeeController { } } + public void setEmployeeConfig(ActionRequest request, ActionResponse response) { + + List employeeIds = (List) request.getContext().get("_ids"); + System.out.println("****************"); + System.out.println(employeeIds); + System.out.println("****************"); + Integer configSelect = (Integer) request.getContext().get("configSelect"); + + if (employeeIds == null || employeeIds.isEmpty()) { + response.setFlash("You haven't selected any Employee."); + } + try { + if (!employeeIds.isEmpty()) { + Beans.get(EmployeeService.class).massUpdateEmployeeConfig(employeeIds, configSelect,true); + response.setReload(true); + } + } catch (Exception e) { + TraceBackService.trace(response, e); + } + } + + public void unSetEmployeeConfig(ActionRequest request, ActionResponse response) { + + List employeeIds = (List) request.getContext().get("_ids"); + System.out.println("****************"); + System.out.println(employeeIds); + System.out.println("****************"); + Integer configSelect = (Integer) request.getContext().get("configSelect"); + + if (employeeIds == null || employeeIds.isEmpty()) { + response.setFlash("You haven't selected any Employee."); + } + try { + if (!employeeIds.isEmpty()) { + Beans.get(EmployeeService.class).massUpdateEmployeeConfig(employeeIds, configSelect,false); + response.setReload(true); + } + + } catch (Exception e) { + TraceBackService.trace(response, e); + } + } + } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/MonthlyReportController.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/MonthlyReportController.java index 4c336d1..e378b52 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/MonthlyReportController.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/MonthlyReportController.java @@ -19,13 +19,17 @@ import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import java.lang.invoke.MethodHandles; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class MonthlyReportController { + private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + public void createMensuelReport(ActionRequest request, ActionResponse response) { try { @@ -68,32 +72,29 @@ public class MonthlyReportController { // Iterate over employees and calculate/update MonthlyReport instances for (Employee employee : employees) { // Filter rapport journaliers for the current employee - List employeeDailyReports = - allDailyReports - .stream() - .filter(r -> r.getEmployee().equals(employee)) - .collect(Collectors.toList()); + List employeeDailyReports = allDailyReports + .stream() + .filter(r -> r.getEmployee().equals(employee)) + .collect(Collectors.toList()); // Filter absences for the current employee - List employeeAbsences = - allAbsences - .stream() - .filter(a -> a.getEmployee().equals(employee)) - .collect(Collectors.toList()); - - System.out.println("Employee: "+ employee.getRegistrationNumber()); + List employeeAbsences = allAbsences + .stream() + .filter(a -> a.getEmployee().equals(employee)) + .collect(Collectors.toList()); if (!employeeDailyReports.isEmpty() || !employeeAbsences.isEmpty()) { - System.out.println("Calculé pour: "+ employee.getRegistrationNumber()); + System.out.println("Create monthly report for employee: "+ employee.getRegistrationNumber()); // Process the employee's monthly report using filtered rapport and absences - Beans.get(MonthlyReportServiceImpl.class) - .createMensuelReport( + Beans.get(MonthlyReportServiceImpl.class).createMensuelReport( employee, period, startDate, endDate, employeeDailyReports, employeeAbsences); + } else { + log.error("No Daily Reports exist for employee: "+ employee.getRegistrationNumber()); } } // Indicate that the action was successful and a reload is needed @@ -176,7 +177,45 @@ public class MonthlyReportController { .bind("endDate", endDate) .bind("employeesIds", employeesIds) .fetch(); - + + for (Employee employee : employees) { + // Check if a MonthlyReport exists for this employee in the specified period + MonthlyReport monthlyReport = Beans.get(MonthlyReportRepository.class) + .all() + .filter("self.employee = :employee and self.period = :period") + .bind("employee", employee) + .bind("period", period) + .fetchOne(); + + Optional monthlyReportOpt = Optional.ofNullable(monthlyReport); + + // Filter daily reports for the current employee + List employeeDailyReports = allDailyReports.stream() + .filter(r -> r.getEmployee().equals(employee)) + .collect(Collectors.toList()); + + // Filter absences for the current employee + List employeeAbsences = allAbsences.stream() + .filter(a -> a.getEmployee().equals(employee)) + .collect(Collectors.toList()); + + if (!employeeDailyReports.isEmpty() || !employeeAbsences.isEmpty()) { + if (monthlyReportOpt.isPresent()) { + MonthlyReport existingReport = monthlyReportOpt.get(); + System.out.println("Update monthly report for employee: " + employee.getRegistrationNumber()); + // Update the existing monthly report + Beans.get(MonthlyReportServiceImpl.class).updateMensuelReport(existingReport, employee, period, startDate, endDate, employeeDailyReports, employeeAbsences); + } else { + System.out.println("Create monthly report for employee: " + employee.getRegistrationNumber()); + // Create a new monthly report + Beans.get(MonthlyReportServiceImpl.class).createMensuelReport(employee, period, startDate, endDate, employeeDailyReports, employeeAbsences); + } + } else { + System.err.println("No Daily Reports exist for employee: " + employee.getRegistrationNumber()); + } + } + + // Indicate that the action was successful and a reload is needed response.setReload(true); } catch (Exception e) { diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Absence.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Absence.xml index 7d27ada..90c4332 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Absence.xml +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Absence.xml @@ -8,12 +8,14 @@ - + + + - + + + + + + + + + + + + + - - - - - - - \ No newline at end of file diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/DailyReport.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/DailyReport.xml index 1dbb4fe..7b8d365 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/DailyReport.xml +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/DailyReport.xml @@ -38,6 +38,11 @@ + + + + + @@ -45,7 +50,6 @@ - @@ -54,6 +58,8 @@ + + @@ -74,6 +80,8 @@ + + @@ -88,6 +96,7 @@ + diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Employee.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Employee.xml index e4922b8..ecc7e58 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Employee.xml +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Employee.xml @@ -89,10 +89,14 @@ + + + + diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/EmployeeFingerPrint.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/EmployeeFingerPrint.xml new file mode 100644 index 0000000..5018f81 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/EmployeeFingerPrint.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/ExtraHours.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/ExtraHours.xml index 919c7d2..738bdca 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/ExtraHours.xml +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/ExtraHours.xml @@ -55,6 +55,7 @@ public static final Integer STATUS_CANCELED = 5; ]]> + diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Shift.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Shift.xml index 5762428..5c2d8f4 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Shift.xml +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Shift.xml @@ -12,14 +12,20 @@