diff --git a/modules/axelor-open-suite/axelor-base/src/main/java/com/axelor/apps/base/web/AppBaseController.java b/modules/axelor-open-suite/axelor-base/src/main/java/com/axelor/apps/base/web/AppBaseController.java index 9321483..db61fa4 100644 --- a/modules/axelor-open-suite/axelor-base/src/main/java/com/axelor/apps/base/web/AppBaseController.java +++ b/modules/axelor-open-suite/axelor-base/src/main/java/com/axelor/apps/base/web/AppBaseController.java @@ -22,12 +22,12 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.InetAddress; +import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.Socket; -import java.net.UnknownHostException; -import java.util.List; +import java.net.URL; +import wslite.json.JSONException; +import wslite.json.JSONObject; import org.apache.batik.util.io.StringDecoder; @@ -48,9 +48,6 @@ import com.axelor.meta.schema.actions.ActionView; import com.axelor.rpc.ActionRequest; import com.axelor.rpc.ActionResponse; import com.google.inject.Singleton; -import com.mongodb.util.JSON; - -import net.minidev.json.JSONObject; @Singleton public class AppBaseController { @@ -174,4 +171,69 @@ public class AppBaseController { Process p = Runtime.getRuntime().exec(args); // BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream())); } + + public static String getAccessToken() throws Exception { + AppSettings appSettings = AppSettings.get(); + String BASE_URL = appSettings.get("portail.api.baseurl"); + String username = appSettings.get("portail.api.user"); + String password = appSettings.get("portail.api.password"); + + String endpoint = BASE_URL + "token/"; + URL url = new URL(endpoint); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Accept", "application/json"); + conn.setDoOutput(true); + + // Create a JSON object for the input + JSONObject jsonInput = new JSONObject(); + jsonInput.put("username", username); + jsonInput.put("password", password); + + // Write the JSON input to the output stream + try (OutputStream os = conn.getOutputStream()) { + byte[] input = jsonInput.toString().getBytes("utf-8"); + os.write(input, 0, input.length); + } + + 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()); + } + return parseAccessToken(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()); + } + } + + // Parse the JSON response to extract the access token + private static String parseAccessToken(String jsonResponse) { + try { + JSONObject jsonObject = new JSONObject(jsonResponse); + return jsonObject.get("access").toString(); // Extract the access token + } catch (JSONException e) { + System.err.println("Failed to parse JSON: " + jsonResponse); + e.printStackTrace(); + return null; // Handle error appropriately + } + } + } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/csv/script/ImportExtraHours.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/csv/script/ImportExtraHours.java new file mode 100644 index 0000000..d87e200 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/csv/script/ImportExtraHours.java @@ -0,0 +1,64 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2019 Axelor (). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.axelor.csv.script; + +import com.axelor.apps.hr.db.Employee; +import com.axelor.apps.hr.db.ExtraHours; +import com.axelor.apps.hr.db.repo.EmployeeRepository; +import com.axelor.apps.hr.db.repo.ExtraHoursRepository; +import com.google.inject.Inject; +import java.util.Map; + +public class ImportExtraHours { + + @Inject private EmployeeRepository employeeRepository; + + @Inject private ExtraHoursRepository extraHoursRepository; + + public Long importEmployee(String registrationNumber) { + Employee employee = + employeeRepository + .all() + .filter("self.registrationNumber = ?1", registrationNumber) + .fetchOne(); + return employee.getId(); + } + + public Object saveExtraHours(Object bean, Map values) { + + if (!(bean instanceof ExtraHours)) { + System.out.println( + "Error: Bean is not an instance of ExtraHours. Actual type: " + + bean.getClass().getName()); + return null; + } + + ExtraHours extraHour = (ExtraHours) bean; + + if (extraHour == null) { + System.out.println("Error: Extra hours data is missing."); + return null; + } + + // Print the values for debugging purposes + System.out.println("Extra hours data found: " + extraHour); + System.out.println("Values map: " + values); + + return extraHoursRepository.save(extraHour); + } +} diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/csv/script/ImportExtraHoursConfiguration.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/csv/script/ImportExtraHoursConfiguration.java new file mode 100644 index 0000000..2d5f44b --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/csv/script/ImportExtraHoursConfiguration.java @@ -0,0 +1,59 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2019 Axelor (). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.axelor.csv.script; + +import com.axelor.apps.base.db.ImportConfiguration; +import com.axelor.meta.MetaFiles; +import com.google.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ImportExtraHoursConfiguration { + + public static final String IMPORT_EXTRA_HOURS_CONFIG = "import-extra-hours-config.xml"; + public static final String IMPORT_EXTRA_HOURS_CSV = "Extra-hours.csv"; + public static final String FILES_DIR = "files"; + + private final Logger log = LoggerFactory.getLogger(ImportExtraHoursConfiguration.class); + @Inject private MetaFiles metaFiles; + + public Object importFiles(Object bean, Map values) { + + assert bean instanceof ImportConfiguration; + + final Path path = (Path) values.get("__path__"); + + ImportConfiguration importConfig = (ImportConfiguration) bean; + + try { + File file = path.resolve(FILES_DIR + File.separator + IMPORT_EXTRA_HOURS_CONFIG).toFile(); + importConfig.setBindMetaFile(metaFiles.upload(file)); + file = path.resolve(FILES_DIR + File.separator + IMPORT_EXTRA_HOURS_CSV).toFile(); + importConfig.setDataMetaFile(metaFiles.upload(file)); + } catch (IOException e) { + log.debug("Error importing Extra-hours import config", e); + return null; + } + + return importConfig; + } +} diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/exception/IExceptionMessage.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/exception/IExceptionMessage.java index 5c1ae32..826e6f4 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/exception/IExceptionMessage.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/exception/IExceptionMessage.java @@ -208,4 +208,8 @@ public interface IExceptionMessage { static final String EMPLOYEE_TIMESHEET_REMINDER_TEMPLATE = /*$$(*/ "Please configure the template for email reminder" /*)*/; + + static final String EXTRA_HOURS_1 = /*$$(*/ "No Extra hours import configuration found" /*)*/; + + static final String EXTRA_HOURS_2 = /*$$(*/ "Import lead" /*)*/; } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/CalculateAttendanceJob.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/CalculateAttendanceJob.java new file mode 100644 index 0000000..068136b --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/CalculateAttendanceJob.java @@ -0,0 +1,63 @@ +package com.axelor.apps.hr.job; + +import com.axelor.apps.hr.db.DailyReport; +import com.axelor.apps.hr.db.repo.DailyReportRepository; +import com.axelor.apps.hr.service.DailyReportServiceImpl; +import com.axelor.exception.service.TraceBackService; +import com.axelor.inject.Beans; +import java.util.List; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.quartz.SchedulerException; + +/** An example {@link Job} class that prints a some messages to the stderr. */ +public class CalculateAttendanceJob implements Job { + + private final Logger log = LoggerFactory.getLogger(CalculateAttendanceJob.class); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException{ + + if (isRunning(context)) { + return; + } + + try { + List dailyReportList = + Beans.get(DailyReportRepository.class) + .all() + .filter("self.isCalculated = ?", false) + .fetch(); + + if (!dailyReportList.isEmpty()) { + Beans.get(DailyReportServiceImpl.class).workingHoursForAll(dailyReportList); + log.debug("Working hours calculated successfully."); + } else { + log.debug("No new records to process."); + } + + } catch (Exception e) { + log.error("An error occurred while calculating working hours."); + TraceBackService.trace(e); + } + } + + private boolean isRunning(JobExecutionContext context) { + try { + return context + .getScheduler() + .getCurrentlyExecutingJobs() + .stream() + .anyMatch( + j -> + j.getTrigger().equals(context.getTrigger()) + && !j.getFireInstanceId().equals(context.getFireInstanceId())); + } catch (SchedulerException e) { + return false; + } + } + +} 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 new file mode 100644 index 0000000..1f4b1e0 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchAttendanceJob.java @@ -0,0 +1,85 @@ +package com.axelor.apps.hr.job; + +import com.axelor.exception.service.TraceBackService; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.quartz.SchedulerException; + +public class FetchAttendanceJob implements Job { + + private final Logger log = LoggerFactory.getLogger(FetchCheckInOutJob.class); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException{ + + if (isRunning(context)) { + 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"}; + + // Execute the command + Process p = Runtime.getRuntime().exec(args); + + // Capture the output stream (standard output) + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + log.info("Python script (Pointage) output: " + line); + } + + // Capture the error stream (standard error) + BufferedReader errorReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + while ((line = errorReader.readLine()) != null) { + log.error("Python script (Pointage) error: " + line); + } + + // Wait for the process to complete and check the exit value + int exitCode = p.waitFor(); + + // Check if the process ran successfully + if (exitCode == 0) { + log.info("Python script executed successfully (Pointage)."); + } else { + log.error("Python script execution (Pointage) failed with exit code: " + exitCode); + } + + } catch (IOException e) { + // Check if the file is not found based on the message or error code + if (e.getMessage().contains("The system cannot find the file specified")) { + log.error("Python script file (Pointage) not found: " + e.getMessage()); + } else { + log.error("An error occurred while executing the Python script (Pointage).", e); + } + TraceBackService.trace(e); + } catch (InterruptedException e) { + // Handle any interruption errors + log.error("Python script (Pointage) execution interrupted.", e); + Thread.currentThread().interrupt(); + } + } + + private boolean isRunning(JobExecutionContext context) { + try { + return context + .getScheduler() + .getCurrentlyExecutingJobs() + .stream() + .anyMatch( + j -> + j.getTrigger().equals(context.getTrigger()) + && !j.getFireInstanceId().equals(context.getFireInstanceId())); + } catch (SchedulerException e) { + return false; + } + } + +} 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 new file mode 100644 index 0000000..4f05438 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchCheckInOutJob.java @@ -0,0 +1,99 @@ +package com.axelor.apps.hr.job; + +import com.axelor.apps.hr.db.CheckInOut; +import com.axelor.apps.hr.db.repo.CheckInOutRepository; +import com.axelor.exception.service.TraceBackService; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.axelor.inject.Beans; +import java.time.LocalDate; +import org.quartz.SchedulerException; + +public class FetchCheckInOutJob implements Job { + + private final Logger log = LoggerFactory.getLogger(FetchCheckInOutJob.class); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + + if (isRunning(context)) { + return; + } + + try { + + LocalDate today = LocalDate.now(); + + // Fetch the CheckInOut list from the repository where the date_attendance is today + int lenCheckInOutList = Beans.get(CheckInOutRepository.class) + .all() + .filter("self.date_attendance = :today") + .bind("today", today) + .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)}; + + // Execute the command + Process p = Runtime.getRuntime().exec(args); + + // Capture the output stream (standard output) + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + log.info("Python script (Check In Out) output: " + line); + } + + // Capture the error stream (standard error) + BufferedReader errorReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + while ((line = errorReader.readLine()) != null) { + log.error("Python script (Check In Out) error: " + line); + } + + // Wait for the process to complete and check the exit value + int exitCode = p.waitFor(); + + // Check if the process ran successfully + if (exitCode == 0) { + log.info("Python script executed successfully (Check In Out)."); + } else { + log.error("Python script execution (Check In Out) failed with exit code: " + exitCode); + } + } catch (IOException e) { + // Check if the file is not found based on the message or error code + if (e.getMessage().contains("The system cannot find the file specified")) { + log.error("Python script file (Check In Out) not found: " + e.getMessage()); + } else { + log.error("An error occurred while executing the Python script (Check In Out).", e); + } + TraceBackService.trace(e); + } catch (InterruptedException e) { + // Handle any interruption errors + log.error("Python script (Check In Out) execution interrupted.", e); + Thread.currentThread().interrupt(); + } + } + + private boolean isRunning(JobExecutionContext context) { + try { + return context + .getScheduler() + .getCurrentlyExecutingJobs() + .stream() + .anyMatch( + j -> + j.getTrigger().equals(context.getTrigger()) + && !j.getFireInstanceId().equals(context.getFireInstanceId())); + } catch (SchedulerException e) { + return false; + } + } + +} 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 new file mode 100644 index 0000000..023bd3c --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/job/FetchHrTicketsJob.java @@ -0,0 +1,214 @@ +package com.axelor.apps.hr.job; + +import com.axelor.apps.base.web.AppBaseController; +import com.axelor.apps.hr.service.AuthorizationService; +import com.axelor.apps.hr.service.extra.hours.ExtraHoursService; +import com.axelor.apps.hr.service.leave.LeaveService; +import com.axelor.apps.hr.web.AuthorizationController; +import com.axelor.apps.hr.web.extra.hours.ExtraHoursController; +import com.axelor.apps.hr.web.leave.LeaveController; +import com.axelor.inject.Beans; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import wslite.json.JSONArray; +import wslite.json.JSONException; +import wslite.json.JSONObject; +import org.quartz.SchedulerException; + +/** An example {@link Job} class that prints a some messages to the stderr. */ +public class FetchHrTicketsJob implements Job { + + private final Logger log = LoggerFactory.getLogger(FetchHrTicketsJob.class); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException{ + + if (isRunning(context)) { + return; + } + + log.info("Starting the job to fetch HR tickets..."); + + String accessToken = null; + try { + accessToken = AppBaseController.getAccessToken(); + } catch (Exception e) { + log.error("Failed to get access token", e); + return; // Exit the method since the access token is critical to proceed + } + + if (accessToken == null) { + log.error("Access token is null, unable to proceed."); + return; + } + + final String token = accessToken; + + // Create a thread pool with a fixed number of threads (can adjust based on system resources) + ExecutorService executorService = Executors.newFixedThreadPool(5); + + try { + // Extra hours fetch task + executorService.submit( + () -> { + try { + log.info("Fetching extra hours data..."); + String jsonResponse = Beans.get(ExtraHoursController.class).fetchHS(token); + log.info("Fetched extra hours data 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(ExtraHoursService.class).saveExtraHours(jsonObject); + } + } catch (JSONException e) { + log.error("Failed to parse JSON: " + jsonResponse, e); + } + } else { + log.warn("No response received from fetchHS."); + } + } catch (Exception e) { + log.error("Failed to fetch extra hours data: ", e); + } + }); + + // Authorization AA fetch task + executorService.submit( + () -> { + try { + log.info("Fetching authorization AA..."); + String jsonResponse = Beans.get(AuthorizationController.class).fetchAA(token); + log.info("Fetched authorization AA 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).saveAA(jsonObject); + } + } catch (JSONException e) { + log.error("Failed to parse JSON: " + jsonResponse, e); + } + } else { + log.warn("No response received from fetchAA."); + } + } catch (Exception e) { + log.error("Failed to fetch authorization AA: ", e); + } + }); + + + // Authorization AE fetch task + executorService.submit( + () -> { + try { + log.info("Fetching authorization AE..."); + String jsonResponse = Beans.get(AuthorizationController.class).fetchAE(token); + log.info("Fetched authorization AE 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).saveAE(jsonObject); + } + } catch (JSONException e) { + log.error("Failed to parse JSON: " + jsonResponse, e); + } + } else { + log.warn("No response received from fetchAE."); + } + } catch (Exception e) { + log.error("Failed to fetch authorization AE: ", e); + } + }); + + // Authorization BS fetch task + executorService.submit( + () -> { + try { + log.info("Fetching authorization BS..."); + String jsonResponse = Beans.get(AuthorizationController.class).fetchBS(token); + log.info("Fetched authorization BS 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).saveBS(jsonObject); + } + } catch (JSONException e) { + log.error("Failed to parse JSON: " + jsonResponse, e); + } + } else { + log.warn("No response received from fetchBS."); + } + } catch (Exception e) { + log.error("Failed to fetch authorization BS: ", e); + } + }); + + // Leave requests fetch task + executorService.submit( + () -> { + try { + log.info("Fetching leave requests..."); + String jsonResponse = Beans.get(LeaveController.class).fetchCR(token); + log.info("Fetched leave requests 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(LeaveService.class).saveCR(jsonObject); + } + } catch (JSONException e) { + log.error("Failed to parse JSON: " + jsonResponse, e); + } + } else { + log.warn("No response received from fetchCR."); + } + } catch (Exception e) { + log.error("Failed to fetch leave requests: ", e); + } + }); + + + } finally { + // Shutdown the ExecutorService gracefully + executorService.shutdown(); + try { + if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + } catch (InterruptedException e) { + executorService.shutdownNow(); + } + } + + log.info("Finished fetching HR tickets."); + } + + private boolean isRunning(JobExecutionContext context) { + try { + return context + .getScheduler() + .getCurrentlyExecutingJobs() + .stream() + .anyMatch( + j -> + j.getTrigger().equals(context.getTrigger()) + && !j.getFireInstanceId().equals(context.getFireInstanceId())); + } catch (SchedulerException e) { + return false; + } + } + +} 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 new file mode 100644 index 0000000..dfc6056 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceService.java @@ -0,0 +1,24 @@ +package com.axelor.apps.hr.service; + +import com.axelor.apps.hr.db.Absence; +import com.axelor.apps.hr.db.DailyReport; +import com.google.inject.persist.Transactional; +import java.util.List; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public interface AbsenceService { + + @Transactional + void attachTheAbsenceWithDailyReport(Absence absence, List dailyreports); + + @Transactional + void deAttachTheAbsenceWithDailyReport(Absence absence, List dailyreports); + + @Transactional + void chooseAbsenceType(Absence absence, String selectedType); + + @Transactional + BigDecimal calculateTotalAbsenceHours(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 new file mode 100644 index 0000000..442125d --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AbsenceServiceImpl.java @@ -0,0 +1,112 @@ +package com.axelor.apps.hr.service; + +import com.axelor.apps.base.db.EventsPlanning; +import com.axelor.apps.base.db.EventsPlanningLine; +import com.axelor.apps.base.db.repo.EventsPlanningRepository; +import com.axelor.apps.base.db.repo.EventsPlanningLineRepository; +import com.axelor.apps.hr.db.Absence; +import com.axelor.apps.hr.db.DailyReport; +import com.axelor.apps.hr.db.repo.AbsenceRepository; +import com.axelor.apps.hr.db.repo.DailyReportRepository; +import com.google.inject.Inject; +import com.google.inject.persist.Transactional; +import com.axelor.inject.Beans; +import java.util.List; +import java.time.Year; +import java.time.DayOfWeek; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +public class AbsenceServiceImpl implements AbsenceService { + + private final AbsenceRepository absenceRepository; + private final DailyReportRepository dailyReportRepository; + private List eventsPlanningLines; + + @Inject + public AbsenceServiceImpl( AbsenceRepository absenceRepository, DailyReportRepository dailyReportRepository) { + this.absenceRepository = absenceRepository; + this.dailyReportRepository = dailyReportRepository; + eventsPlanningLines = + Beans.get(EventsPlanningLineRepository.class) + .all() + .filter("self.id = 4") + .fetch(); + } + + @Transactional + public void attachTheAbsenceWithDailyReport( + Absence absence, List dailyreports) { + // Iterate over each DailyReport in the list + for (DailyReport dailyreport : dailyreports) { + dailyreport.setAbsence(absence); // Set the absence for each report + dailyReportRepository.save(dailyreport); + } + } + + @Transactional + public void deAttachTheAbsenceWithDailyReport(Absence absence, List dailyreports) { + if(dailyreports != null){ + // Iterate over each DailyReport in the list + for (DailyReport dailyreport : dailyreports) { + dailyreport.setAbsence(null); // Set the absence for each report + dailyReportRepository.save(dailyreport); + } + } + + absence.setArchived(true); + absenceRepository.save(absence); + } + + @Transactional + public void chooseAbsenceType(Absence absence, String selectedType) { + // Set the selected absence type to the absence entity + // Assuming you have a field named 'typeAbsence' in the Absence entity + absence.setAbsenceType(selectedType); + + // Save the absence entity + absenceRepository.save(absence); + } + + public BigDecimal calculateTotalAbsenceHours(LocalDateTime absenceStartDate, LocalDateTime absenceEndDate) { + BigDecimal totalAbsenceHours = BigDecimal.ZERO; + + if(absenceStartDate.equals(absenceEndDate)){ + totalAbsenceHours = BigDecimal.valueOf(8); + } + else if (absenceStartDate.toLocalDate().equals(absenceEndDate.toLocalDate())) { + // If the start and end dates are the same, calculate the hour difference + long hours = ChronoUnit.HOURS.between(absenceStartDate, absenceEndDate); + totalAbsenceHours = BigDecimal.valueOf(hours); + } else { + long absenceDays = 0; + LocalDate currentDate = absenceStartDate.toLocalDate(); + + // Loop through each day between start and end date + while (!currentDate.isAfter(absenceEndDate.toLocalDate())) { + DayOfWeek dayOfWeek = currentDate.getDayOfWeek(); + + // Exclude Friday (5) and Saturday (6) and holidays + if (dayOfWeek != DayOfWeek.FRIDAY && dayOfWeek != DayOfWeek.SATURDAY && !isSpecialOvertimeDay(currentDate)) { + absenceDays++; + } + currentDate = currentDate.plusDays(1); + } + + // Multiply the counted days by 8 hours per day + totalAbsenceHours = BigDecimal.valueOf(absenceDays).multiply(BigDecimal.valueOf(8)); + } + return totalAbsenceHours; + } + + private boolean isSpecialOvertimeDay(LocalDate date) { + for (EventsPlanningLine line : eventsPlanningLines) { + if (line.getDate().equals(date)) { + return true; + } + } + return false; + } +} 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 new file mode 100644 index 0000000..2c58355 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationService.java @@ -0,0 +1,17 @@ +package com.axelor.apps.hr.service; + +import com.axelor.exception.AxelorException; +import com.google.inject.persist.Transactional; +import wslite.json.JSONObject; + +public interface AuthorizationService { + + @Transactional(rollbackOn = {Exception.class}) + public void saveAA(JSONObject jsonObject) throws AxelorException; + + @Transactional(rollbackOn = {Exception.class}) + public void saveAE(JSONObject jsonObject) throws AxelorException; + + @Transactional(rollbackOn = {Exception.class}) + public void saveBS(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 new file mode 100644 index 0000000..8aa7dd0 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/AuthorizationServiceImpl.java @@ -0,0 +1,498 @@ +package com.axelor.apps.hr.service; + +import com.axelor.apps.hr.db.Authorization; +import com.axelor.apps.hr.db.Employee; +import com.axelor.apps.hr.db.DailyReport; +import com.axelor.apps.hr.db.repo.AuthorizationRepository; +import com.axelor.apps.hr.db.repo.EmployeeRepository; +import com.axelor.apps.hr.db.repo.DailyReportRepository; +import com.axelor.exception.AxelorException; +import com.google.inject.persist.Transactional; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import wslite.json.JSONException; +import wslite.json.JSONObject; +import java.util.Set; +import java.util.HashSet; + +public class AuthorizationServiceImpl implements AuthorizationService { + + protected EmployeeRepository employeeRepo; + protected AuthorizationRepository authorizationRepository; + protected DailyReportRepository dailyReportRepo; + + @Inject + public AuthorizationServiceImpl( + EmployeeRepository employeeRepo, + AuthorizationRepository authorizationRepository, + DailyReportRepository dailyReportRepo) { + this.employeeRepo = employeeRepo; + this.authorizationRepository = authorizationRepository; + this.dailyReportRepo = dailyReportRepo; + } + + @Transactional(rollbackOn = {Exception.class}) + public void saveAA(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_absence"); + String commentaire = jsonObject.getString("commentaire"); + int validation_status = jsonObject.optInt("validation_status",2); + String validateByUser = jsonObject.optString("validate_by_user",null); + String dateValidation = jsonObject.optString("validation_date", null); // Safely get validation_date + + // 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 absence 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.setIsAuthorizedAbsence(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 { + // Authorization does not exist, create a new one as per the validation_status logic + authorization = new Authorization(); + authorization.setEmployee(employee); + authorization.setTicketId(idInt); + authorization.setTicket(ticketLink); + authorization.setRequisitionDate(absenceDate); + authorization.setDescription(commentaire); + authorization.setStatusSelect(validation_status); + authorization.setAuthorizationType(2); // Set default authorization type + + 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.setIsAuthorizedAbsence(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 saveAE(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_reprise"); + String commentaire = jsonObject.getString("commentaire"); + 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.setIsAuthorizedAbsence(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 { + // Authorization does not exist, create a new one as per the validation_status logic + authorization = new Authorization(); + authorization.setEmployee(employee); + authorization.setTicketId(idInt); + authorization.setTicket(ticketLink); + authorization.setRequisitionDate(absenceDate); + authorization.setDescription(commentaire); + authorization.setStatusSelect(validation_status); + authorization.setAuthorizationType(1); // Set default authorization type + + 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.setIsAuthorizedAbsence(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 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.setIsAuthorizedAbsence(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.setIsAuthorizedAbsence(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(); + } + } + + // Helper function to parse dates + private LocalDate parseDate(String dateString) { + try { + OffsetDateTime offsetDateTime = OffsetDateTime.parse(dateString, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + return offsetDateTime.toLocalDate(); + } catch (DateTimeParseException e) { + System.out.println("Error parsing date: " + dateString); + return null; + } +} +} 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 new file mode 100644 index 0000000..db65660 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportService.java @@ -0,0 +1,13 @@ +package com.axelor.apps.hr.service; + +import com.axelor.apps.hr.db.DailyReport; +import com.google.inject.persist.Transactional; + +public interface DailyReportService { + + @Transactional + void workingHours(DailyReport DailyReport); + + @Transactional + void determineShift(DailyReport DailyReport); +} 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 new file mode 100644 index 0000000..30a4599 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/DailyReportServiceImpl.java @@ -0,0 +1,548 @@ +package com.axelor.apps.hr.service; + +import com.axelor.apps.base.db.EventsPlanning; +import com.axelor.apps.base.db.EventsPlanningLine; +import com.axelor.apps.base.db.repo.EventsPlanningRepository; +import com.axelor.apps.base.db.repo.EventsPlanningLineRepository; +import com.axelor.apps.hr.db.Absence; +import com.axelor.apps.hr.db.Employee; +import com.axelor.apps.hr.db.OffDayWork; +import com.axelor.apps.hr.db.DailyReport; +import com.axelor.apps.hr.db.Shift; +import com.axelor.apps.hr.db.Authorization; +import com.axelor.apps.hr.db.repo.AbsenceRepository; +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.inject.Beans; +import com.google.inject.persist.Transactional; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Year; +import java.util.List; +import com.google.inject.Inject; + +public class DailyReportServiceImpl implements DailyReportService { + + private final DailyReportRepository dailyReportRepository; + private final AuthorizationRepository autorizationRepository; + private final AbsenceRepository absenceRepository; + private final String HOLIDAYS_STRING = "Planning jours de Féries DZ"; + private EventsPlanning eventsPlanning; + private List eventsPlanningLines; + private static final LocalTime NIGHT_START = LocalTime.of(21, 0); // 9:00 PM + private static final LocalTime NIGHT_END = LocalTime.of(5, 0); // 5:00 AM + + private LocalTime SHIFT_8h16_min; + private LocalTime SHIFT_8h16_max; + private LocalTime SHIFT_14h22_min; + private LocalTime SHIFT_14h22_max; + private LocalTime SHIFT_22h6_min; + private LocalTime SHIFT_22h6_max; + private LocalTime SHIFT_6h14_min; + private LocalTime SHIFT_6h14_max; + + @Inject + public DailyReportServiceImpl(DailyReportRepository dailyReportRepository, AuthorizationRepository autorizationRepository, AbsenceRepository absenceRepository) { + this.dailyReportRepository = dailyReportRepository; + this.autorizationRepository = autorizationRepository; + this.absenceRepository = absenceRepository; + eventsPlanningLines = Beans.get(EventsPlanningLineRepository.class) + .all() + .filter("self.eventsPlanning = 4") + .fetch(); + + SHIFT_8h16_min = Beans.get(ShiftRepository.class).all().filter("self.shift = 0").fetchOne().getEnterMin(); + SHIFT_8h16_max = Beans.get(ShiftRepository.class).all().filter("self.shift = 0").fetchOne().getEnterMax(); + SHIFT_14h22_min = Beans.get(ShiftRepository.class).all().filter("self.shift = 1").fetchOne().getEnterMin(); + SHIFT_14h22_max = Beans.get(ShiftRepository.class).all().filter("self.shift = 1").fetchOne().getEnterMax(); + SHIFT_22h6_min = Beans.get(ShiftRepository.class).all().filter("self.shift = 2").fetchOne().getEnterMin(); + SHIFT_22h6_max = Beans.get(ShiftRepository.class).all().filter("self.shift = 2").fetchOne().getEnterMax(); + SHIFT_6h14_min = Beans.get(ShiftRepository.class).all().filter("self.shift = 3").fetchOne().getEnterMin(); + SHIFT_6h14_max = Beans.get(ShiftRepository.class).all().filter("self.shift = 3").fetchOne().getEnterMax(); + } + + @Override + @Transactional + public void workingHours(DailyReport dailyReport) { + + LocalDateTime[] enters = { + dailyReport.getEnter1(), dailyReport.getEnter2(), dailyReport.getEnter3(), + dailyReport.getEnter4(), dailyReport.getEnter5(), dailyReport.getEnter6() + }; + LocalDateTime[] quits = { + dailyReport.getQuit1(), dailyReport.getQuit2(), dailyReport.getQuit3(), + dailyReport.getQuit4(), dailyReport.getQuit5(), dailyReport.getQuit6() + }; + + Employee employee = dailyReport.getEmployee(); + LocalDate reportDate = dailyReport.getReportDate(); + Integer shift = dailyReport.getShift().getShift(); + + LocalTime shiftStartHour = null, shiftEndHour = 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); + } + + if (enters[0] != null && quits[0] != null) { + // Calculate total work duration + Duration totalDuration = Duration.ZERO; + Duration totalSupDuration = Duration.ZERO; + Duration nightDuration = Duration.ZERO; + Duration pauseDuration = Duration.ZERO; + LocalDateTime firstEnter = enters[0]; + LocalDateTime lastQuit = quits[0]; + + for (int i = 0; i < enters.length; i++) { + if (enters[i] != null && quits[i] != null) { + // totalDuration = totalDuration.plus(Duration.between(enters[i], quits[i])); + lastQuit = quits[i]; + } + } + + 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)) { + long minutesLate = Duration.between(shiftStartHour, firstEnterTime).toMinutes(); + BigDecimal lateArrival = BigDecimal.valueOf(minutesLate).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + dailyReport.setLateArrival(lateArrival); + } + } + + // Calculate early departure if lastQuit is earlier than shift end + if(shiftEndHour != null){ + if (lastQuitTime.isBefore(shiftEndHour)) { + long minutesEarly = Duration.between(lastQuitTime, shiftEndHour).toMinutes(); + BigDecimal earlyDeparture = BigDecimal.valueOf(minutesEarly).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + dailyReport.setEarlyDeparture(earlyDeparture); + } + } + + // Total hours + 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)); + + // 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)); + + // 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)); + + // shift 2 + if (shift == 2) { + BigDecimal extraHours50 = BigDecimal.ZERO; + BigDecimal extraHours100 = BigDecimal.ZERO; + + DayOfWeek enterDay = firstEnter.getDayOfWeek(); + DayOfWeek quitDay = lastQuit.getDayOfWeek(); + + LocalDateTime midnight = firstEnter.toLocalDate().atStartOfDay().plusDays(1); + + // Calculate time from first enter to midnight + Duration beforeMidnightDuration = Duration.between(firstEnter, midnight); + long beforeMidnightMinutes = beforeMidnightDuration.toMinutes(); + BigDecimal beforeMidnightHours = BigDecimal.valueOf(beforeMidnightMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + + // Calculate time from midnight to last quit + Duration afterMidnightDuration = Duration.between(midnight, lastQuit); + long afterMidnightMinutes = afterMidnightDuration.toMinutes(); + BigDecimal afterMidnightHours = BigDecimal.valueOf(afterMidnightMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + + if (enterDay == DayOfWeek.THURSDAY && quitDay == DayOfWeek.FRIDAY || isSpecialOvertimeDay(lastQuit)) { + extraHours100 = afterMidnightHours; + dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + if (enterDay == DayOfWeek.THURSDAY && quitDay == DayOfWeek.FRIDAY) + // No Extra Hours ticket needed + dailyReport.setIsValidSupHours(true); + } else if (isSpecialOvertimeDay(firstEnter)) { + + // Add recup + if (totalHours.compareTo(BigDecimal.valueOf(6)) >= 0) + createOffDayWork(reportDate, employee); + extraHours100 = beforeMidnightHours; + dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + } else if (enterDay == DayOfWeek.FRIDAY && quitDay == DayOfWeek.SATURDAY) { + + // Add recup + if (totalHours.compareTo(BigDecimal.valueOf(6)) >= 0) + createOffDayWork(reportDate, employee); + extraHours100 = beforeMidnightHours; + extraHours50 = afterMidnightHours; + dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + + } else if ((enterDay == DayOfWeek.SATURDAY && quitDay == DayOfWeek.SUNDAY)) { + extraHours50 = beforeMidnightHours; + dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + } else { + totalSupDuration = calculateSupplementaryHours(firstEnter,lastQuit,shift,reportDate); + long totalSupMinutes = totalSupDuration.toMinutes(); + extraHours50 = BigDecimal.valueOf(totalSupMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + } + + dailyReport.setExtraHours100(customRound(extraHours100)); + dailyReport.setExtraHours50(customRound(extraHours50)); + + // other shifts + } else { + // Calculate supplementary hours + totalSupDuration = calculateSupplementaryHours(firstEnter, lastQuit, shift, reportDate); + long totalSupMinutes = totalSupDuration.toMinutes(); + BigDecimal totalSupHours = BigDecimal.valueOf(totalSupMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + + // Holidays and weekends + if (firstEnter.getDayOfWeek() == DayOfWeek.SATURDAY) { + + if(shift == 0 || shift == 3){ + dailyReport.setExtraHours50(customRound(totalHours).subtract(customRound(totalNightHours))); + dailyReport.setExtraHours100(customRound(totalNightHours)); + dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + } else { + dailyReport.setExtraHours50(customRound(totalHours)); + dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + } + + } else if (firstEnter.getDayOfWeek() == DayOfWeek.FRIDAY || isSpecialOvertimeDay(firstEnter)) { + + // Add recup + if (totalHours.compareTo(BigDecimal.valueOf(6)) >= 0) + createOffDayWork(reportDate, employee); + dailyReport.setExtraHours100(customRound(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.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.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + dailyReport.setAllowanceRecall(totalSupHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + } + } + } + + // Calculate ITP + dailyReport.setItp(customRound(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); + } else if (enters.length != quits.length) { + + Duration totalDuration = Duration.ZERO; + LocalDateTime firstEnter = enters[0]; + LocalDateTime 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.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0); + + dailyReport.setLastQuit(lastQuit); + LocalTime firstEnterTime = firstEnter.toLocalTime(); + LocalTime lastQuitTime = lastQuit.toLocalTime(); + // Calculate late arrival if firstEnter is later than shift start + if (firstEnterTime.isAfter(shiftStartHour)) { + long minutesLate = Duration.between(shiftStartHour, firstEnterTime).toMinutes(); + BigDecimal lateArrival = BigDecimal.valueOf(minutesLate).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + dailyReport.setLateArrival(lateArrival); + } + + // Calculate early departure if lastQuit is earlier than shift end + if (lastQuitTime.isBefore(shiftEndHour)) { + long minutesEarly = Duration.between(lastQuitTime, shiftEndHour).toMinutes(); + BigDecimal earlyDeparture = BigDecimal.valueOf(minutesEarly).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); + dailyReport.setEarlyDeparture(earlyDeparture); + } + + } else if (employee == null) { + System.err.println("Warning: Employee doesn't exist."); + dailyReport.setIsCalculated(true); + } else { + System.err.println("Warning: no attendances for this day."); + dailyReport.setIsCalculated(true); + } + + // Absences + if (dailyReport.getAbsence() == null) { + Absence absence = Beans.get(AbsenceRepository.class) + .all() + .filter("self.employee = :employee and self.startDate <= :reportDate and self.endDate >= :reportDate") + .bind("employee", employee) + .bind("reportDate",reportDate) // Changed from absenceStartDate and absenceEndDate to reportDate + .fetchOne(); + + if (absence != null) { + dailyReport.setAbsence(absence); + } + } + + // 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); + } + } + + // Holidays + if (isSpecialOvertimeDay(reportDate.atStartOfDay())) { + dailyReport.setIsFerieDay(true); + } + + // Weekends + if (reportDate.getDayOfWeek() == DayOfWeek.FRIDAY || reportDate.getDayOfWeek() == DayOfWeek.SATURDAY) { + dailyReport.setIsWeekend(true); + } + + // Absence Hours + if (!(dailyReport.getIsWeekend() || dailyReport.getIsFerieDay()) && dailyReport.getWorkHours().compareTo(BigDecimal.valueOf(8)) < 0) { + dailyReport.setAbsenceHours(customRound(BigDecimal.valueOf(8).subtract(dailyReport.getWorkHours()))); + } else { + dailyReport.setAbsenceHours(BigDecimal.ZERO); + } + + dailyReportRepository.save(dailyReport); + } + + @Transactional + public void determineShift(DailyReport dailyReport){ + LocalDateTime enters1 = dailyReport.getEnter1(); + if(enters1 != null){ + // Extract time from enters1 + LocalTime firstTime = enters1.toLocalTime(); + + // Define shifts + Shift shift; + if (firstTime.isAfter(SHIFT_6h14_min) && firstTime.isBefore(SHIFT_6h14_max)) { + shift = Beans.get(ShiftRepository.class).find(3L); // Shift 6h14 + } else if (firstTime.isAfter(SHIFT_8h16_min) && firstTime.isBefore(SHIFT_8h16_max)) { + shift = Beans.get(ShiftRepository.class).find(4L); // Shift 8h16 + } else if (firstTime.isAfter(SHIFT_14h22_min) && firstTime.isBefore(SHIFT_14h22_max)) { + shift = Beans.get(ShiftRepository.class).find(1L); // Shift 14h22 + } else if (firstTime.isAfter(SHIFT_22h6_min) || firstTime.isBefore(SHIFT_22h6_max)) { + shift = Beans.get(ShiftRepository.class).find(2L); // Shift 22h6 + } else { + shift = Beans.get(ShiftRepository.class).find(50L); // N/A + } + dailyReport.setShift(shift); + dailyReportRepository.save(dailyReport); + } + } + + public void workingHoursForAll(List DailyReportList) { + for (int i = 0; i < DailyReportList.size(); i++) { + // System.out.println(DailyReportList.get(i).getId()); + workingHours(DailyReportList.get(i)); + } + System.out.println("Calculation Done"); + } + + private boolean isSpecialOvertimeDay(LocalDateTime date) { + LocalDate givenDate = date.toLocalDate(); + for (EventsPlanningLine line : eventsPlanningLines) { + if (line.getDate().equals(givenDate)) { + return true; + } + } + return false; + } + + private Duration calculateNightDuration(LocalDateTime enter, LocalDateTime quit) { + + // Create LocalDateTime objects for night start and end + LocalDateTime nightStartDateTime = enter.toLocalDate().atTime(NIGHT_START); + LocalDateTime nightEndDateTime = enter.toLocalDate().plusDays(1).atTime(NIGHT_END); + + // Initialize night duration to zero + Duration nightDuration1 = Duration.ZERO; + + // Check if there is an overlap with the night period + if (enter.isBefore(nightEndDateTime) && quit.isAfter(nightStartDateTime)) { + + // Calculate effective start and end times for night hours + LocalDateTime effectiveStart = + enter.isBefore(nightStartDateTime) ? nightStartDateTime : enter; + LocalDateTime effectiveEnd = quit.isAfter(nightEndDateTime) ? nightEndDateTime : quit; + + // Calculate and return the night duration + if (effectiveStart.isBefore(effectiveEnd)) { + nightDuration1 = Duration.between(effectiveStart, effectiveEnd); + } + } + + return nightDuration1; + } + + private Duration calculateSupplementaryHours(LocalDateTime enter, LocalDateTime quit, int shift, LocalDate reportDate) { + + // Calculate Supp hours for (0,1,2,3) shifts + Shift shiftInstance = + Beans.get(ShiftRepository.class) + .all() + .filter("self.shift = :shift") + .bind("shift", shift) + .fetchOne(); + + LocalTime shiftStart = shiftInstance.getStartHour(); + LocalTime shiftEnd = shiftInstance.getEndHour(); + + // Create LocalDateTime objects for shift start and end + LocalDateTime shiftStartDateTime = reportDate.atTime(shiftStart); + LocalDateTime shiftEndDateTime = quit.toLocalDate().atTime(shiftEnd); + + // Initialize supplementary duration to zero + Duration supDuration = Duration.ZERO; + + // Calculate supplementary duration before shift start + if (enter.isBefore(shiftStartDateTime)) { + Duration beforeShiftDuration = Duration.between(enter, shiftStartDateTime); + if (beforeShiftDuration.toMinutes() > 30) { + supDuration = supDuration.plus(beforeShiftDuration); + } + } + + // Calculate supplementary duration after shift end + if (quit.isAfter(shiftEndDateTime)) { + Duration afterShiftDuration = Duration.between(shiftEndDateTime, quit); + if (afterShiftDuration.toMinutes() > 30) { + supDuration = supDuration.plus(afterShiftDuration); + } + } + + return supDuration; + } + + private Duration calculatePauseDuration( + LocalDateTime[] enters, LocalDateTime[] quits, Employee employee) { + + Duration pauseDuration = 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])); + } + } + return pauseDuration; + } + + private BigDecimal calculateItp(BigDecimal totalHours, Integer shift, Boolean hasItp) { + // Shift 0 (no itp) + if (hasItp == false) + return BigDecimal.ZERO; + else + return totalHours.min(BigDecimal.valueOf(8)); + } + + private void createOffDayWork(LocalDate reportDate, Employee employee) { + + // Vérification si l'enregistrement existe déjà + OffDayWork existingOffDayWork = + Beans.get(OffDayWorkRepository.class) + .all() + .filter("self.employee = :employee AND self.offDay = :reportDate") + .bind("employee", employee) + .bind("reportDate", reportDate) + .fetchOne(); + + if (existingOffDayWork == null) { + // Si aucun enregistrement n'est trouvé, créer un nouveau OffDayWork + OffDayWork offDayWork = new OffDayWork(); + offDayWork.setEmployee(employee); + offDayWork.setOffDay(reportDate); + Beans.get(OffDayWorkRepository.class).save(offDayWork); + } + } + + public boolean checkShiftChange(Employee employee, LocalDate reportDate, Integer currentShift) { + + LocalDate firstDayOfWeek = getPreviousWeekSunday(reportDate); + DailyReport rapportOfFirstDayOfWeek = + dailyReportRepository + .all() + .filter("self.employee = :employee and self.reportDate = :firstDayOfWeek") + .bind("employee", employee) + .bind("firstDayOfWeek", firstDayOfWeek) + .fetchOne(); + + Integer firstShift = rapportOfFirstDayOfWeek.getShift().getShift(); + // Corrected print statements + System.out.println("The day: " + reportDate + " , Shift: " + currentShift); + System.out.println("The Sunday day: " + firstDayOfWeek + " , Shift: " + firstShift); + System.out.println("Result: " + !currentShift.equals(firstShift)); + return !currentShift.equals(firstShift); + } + + // private Duration calculateAbsencesHours(LocalDateTime quit, int shift){ } + private static LocalDate getPreviousWeekSunday(LocalDate date) { + if (date.getDayOfWeek() == DayOfWeek.SUNDAY) { + return date; + } + + // Find the day of week for the given date + DayOfWeek dayOfWeek = date.getDayOfWeek(); + // Calculate the number of days to the previous Sunday + int daysToPreviousSunday = dayOfWeek.getValue() + 7 - DayOfWeek.SUNDAY.getValue(); + // Calculate the previous Sunday date + return date.minusDays(daysToPreviousSunday); + } + + 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/MonthlyReportService.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportService.java new file mode 100644 index 0000000..b347b09 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportService.java @@ -0,0 +1,23 @@ +package com.axelor.apps.hr.service; + +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.google.inject.persist.Transactional; +import java.time.LocalDate; +import java.util.List; + +public interface MonthlyReportService { + + @Transactional + void createMensuelReport( + 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 new file mode 100644 index 0000000..8b2fdcc --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/MonthlyReportServiceImpl.java @@ -0,0 +1,309 @@ +package com.axelor.apps.hr.service; + +import com.axelor.apps.base.db.Period; +import com.axelor.apps.hr.db.Absence; +import com.axelor.apps.hr.db.Authorization; +import com.axelor.apps.hr.db.Employee; +import com.axelor.apps.hr.db.LeaveRequest; +import com.axelor.apps.hr.db.MonthlyReport; +import com.axelor.apps.hr.db.DailyReport; +import com.axelor.apps.hr.db.repo.AbsenceRepository; +import com.axelor.apps.hr.db.repo.AuthorizationRepository; +import com.axelor.apps.hr.db.repo.MonthlyReportRepository; +import com.axelor.apps.hr.db.repo.DailyReportRepository; +import com.google.inject.persist.Transactional; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; +import java.util.List; +import javax.inject.Inject; +import com.axelor.inject.Beans; + +public class MonthlyReportServiceImpl implements MonthlyReportService { + + private final MonthlyReportRepository monthlyReportRepository; + private final DailyReportRepository dailyReportRepository; + private final AuthorizationRepository authorizationRepository; + private final AbsenceRepository absenceRepository; + + @Inject + public MonthlyReportServiceImpl( + MonthlyReportRepository monthlyReportRepository, + DailyReportRepository dailyReportRepository, + AuthorizationRepository authorizationRepository, + AbsenceRepository absenceRepository) { + + this.monthlyReportRepository = monthlyReportRepository; + this.dailyReportRepository = dailyReportRepository; + this.authorizationRepository = authorizationRepository; + this.absenceRepository = absenceRepository; + } + + @Transactional + @Override + public void createMensuelReport(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(); + BigDecimal heureSup100 = dailyReport.getExtraHours100(); + BigDecimal itp = dailyReport.getItp(); + BigDecimal nuissance = dailyReport.getNuissance(); + + 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()); + + 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(); + LeaveRequest leaveRequest = dailyReport.getLeaveRequest(); + Absence absence = dailyReport.getAbsence(); + + 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; + } + } + } + + // Update or create MonthlyReport instance with calculated values + MonthlyReport monthlyReport = new MonthlyReport(); + 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) { + BigDecimal primeAssiduite = BigDecimal.ZERO; + BigDecimal excessHours50 = MonthlyReport.getExcessHours50(); + BigDecimal excessHours100 = MonthlyReport.getExcessHours100(); + + // Calculate primeAssiduite using the provided formula + BigDecimal salaireBase = monthlyReport.getEmployee().getSalaireBase(); + BigDecimal supHours50Bonus = excessHours50.multiply(salaireBase + .multiply(new BigDecimal(1.5).setScale(2, RoundingMode.HALF_UP)) + .divide(new BigDecimal(176), 2, RoundingMode.HALF_UP) + .setScale(2, RoundingMode.HALF_UP)) + .setScale(2, RoundingMode.HALF_UP); + + BigDecimal supHours100Bonus = excessHours100.multiply(salaireBase + .multiply(new BigDecimal(2).setScale(2, RoundingMode.HALF_UP)) + .divide(new BigDecimal(176), 2, RoundingMode.HALF_UP) + .setScale(2, RoundingMode.HALF_UP)) + .setScale(2, RoundingMode.HALF_UP); + + primeAssiduite = supHours50Bonus.add(supHours100Bonus); + + monthlyReport.setPrimeAssiduite(primeAssiduite.setScale(2, RoundingMode.HALF_UP)); + // Save the MonthlyReport instance + monthlyReportRepository.save(monthlyReport); + } + */ +} diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/extra/hours/ExtraHoursService.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/extra/hours/ExtraHoursService.java index b01e402..0ebe429 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/extra/hours/ExtraHoursService.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/extra/hours/ExtraHoursService.java @@ -23,6 +23,7 @@ import com.axelor.exception.AxelorException; import com.google.inject.persist.Transactional; import java.io.IOException; import javax.mail.MessagingException; +import wslite.json.JSONObject; public interface ExtraHoursService { @@ -50,6 +51,9 @@ public interface ExtraHoursService { @Transactional(rollbackOn = {Exception.class}) public void refuse(ExtraHours extraHours) throws AxelorException; + @Transactional(rollbackOn = {Exception.class}) + public void saveExtraHours(JSONObject jsonObject) throws AxelorException; + public Message sendRefusalEmail(ExtraHours extraHours) throws AxelorException, ClassNotFoundException, InstantiationException, IllegalAccessException, MessagingException, IOException; diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/extra/hours/ExtraHoursServiceImpl.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/extra/hours/ExtraHoursServiceImpl.java index ea01d99..45d9997 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/extra/hours/ExtraHoursServiceImpl.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/extra/hours/ExtraHoursServiceImpl.java @@ -17,6 +17,13 @@ */ package com.axelor.apps.hr.service.extra.hours; + +import com.axelor.apps.hr.db.DailyReport; +import com.axelor.apps.hr.db.repo.EmployeeRepository; +import com.axelor.apps.hr.db.repo.DailyReportRepository; +import com.axelor.apps.hr.db.Employee; +import com.axelor.apps.base.db.Company; +import com.axelor.apps.base.db.repo.CompanyRepository; import com.axelor.apps.base.service.app.AppBaseService; import com.axelor.apps.hr.db.ExtraHours; import com.axelor.apps.hr.db.ExtraHoursLine; @@ -30,9 +37,17 @@ import com.axelor.exception.AxelorException; import com.google.inject.Inject; import com.google.inject.persist.Transactional; import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; import java.math.BigDecimal; import java.util.List; import javax.mail.MessagingException; +import wslite.json.JSONException; +import wslite.json.JSONObject; public class ExtraHoursServiceImpl implements ExtraHoursService { @@ -40,15 +55,24 @@ public class ExtraHoursServiceImpl implements ExtraHoursService { protected AppBaseService appBaseService; protected HRConfigService hrConfigService; protected TemplateMessageService templateMessageService; + protected CompanyRepository companyRepo; + protected DailyReportRepository dailyReportRepo; + protected EmployeeRepository employeeRepo; @Inject public ExtraHoursServiceImpl( + EmployeeRepository employeeRepo, + CompanyRepository companyRepo, + DailyReportRepository dailyReportRepo, ExtraHoursRepository extraHoursRepo, AppBaseService appBaseService, HRConfigService hrConfigService, TemplateMessageService templateMessageService) { this.extraHoursRepo = extraHoursRepo; + this.employeeRepo = employeeRepo; + this.companyRepo = companyRepo; + this.dailyReportRepo = dailyReportRepo; this.appBaseService = appBaseService; this.hrConfigService = hrConfigService; this.templateMessageService = templateMessageService; @@ -162,4 +186,188 @@ public class ExtraHoursServiceImpl implements ExtraHoursService { extraHours.setTotalQty(totalQty); } + + @Transactional(rollbackOn = {Exception.class}) + public void saveExtraHours(JSONObject jsonObject) throws AxelorException { + Company company = companyRepo.all().filter("self.id = 1").fetchOne(); + 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 dateRequisition = jsonObject.getString("date_requisition"); + String motifRequisition = jsonObject.getString("motif_requisition"); + String heureDebut = jsonObject.getString("heure_debut"); + String heureFin = jsonObject.getString("heure_fin"); + String lieuTravail = jsonObject.getString("lieu_travail"); + 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 requisitionDate; + try { + requisitionDate = LocalDate.parse(dateRequisition); + } catch (DateTimeParseException e) { + System.out.println("Error parsing dateRequisition: " + dateRequisition); + requisitionDate = null; + } + + // Check if Authorization exists by ticketId + ExtraHours extraHours = extraHoursRepo + .all() + .filter("self.ticketId = :ticketId") + .bind("ticketId", idInt) + .fetchOne(); + + if (extraHours != null) { + // Authorization exists, compare previous and new status + int previousStatus = extraHours.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 + extraHours.setValidatedByEmployee(validatedByEmployee); + extraHours.setValidationDate(validationDate); + extraHours.setStatusSelect(newStatus); + // Save the updated Authorization + extraHoursRepo.save(extraHours); + + // Get Daily report + DailyReport dailyReport = + dailyReportRepo + .all() + .filter("self.employee = :employee and self.reportDate = :reportDate") + .bind("employee", employee) + .bind("reportDate", requisitionDate) + .fetchOne(); + + if (dailyReport != null) { + dailyReport.setIsValidSupHours(true); + } + } else if (previousStatus == 2 && newStatus == 4) { + System.out.println("Tickets :" + idInt + " Status changed from " + previousStatus + " to " + newStatus); + extraHours.setRefusedByEmployee(validatedByEmployee); + extraHours.setRefusalDate(validationDate); + extraHours.setStatusSelect(newStatus); + // Save the updated Authorization + extraHoursRepo.save(extraHours); + } + } else { + // Create an instance of ExtraHours + extraHours = new ExtraHours(); + extraHours.setEmployee(employee); + extraHours.setTicket(ticketLink); + extraHours.setTicketId(idInt); + extraHours.setRequisitionDate(requisitionDate); + extraHours.setDescription(motifRequisition); + extraHours.setStatusSelect(validation_status); + extraHours.setCompany(company); + + // Parse Start and End Hours + try { + extraHours.setStartHour(LocalTime.parse(heureDebut)); + } catch (DateTimeParseException e) { + // Append the problematic heureDebut to the description + String updatedDescription = extraHours.getDescription() + " - " + heureDebut; + extraHours.setDescription(updatedDescription); + extraHours.setStartHour(LocalTime.MIDNIGHT); // Optionally set a default value + } + + try { + extraHours.setEndHour(LocalTime.parse(heureFin)); + } catch (DateTimeParseException e) { + // Append the problematic heureFin to the description + String updatedDescription = extraHours.getDescription() + " - " + heureFin; + extraHours.setDescription(updatedDescription); + extraHours.setEndHour(LocalTime.MIDNIGHT); // Optionally set a default value + } + + if (validation_status == 3) { + extraHours.setValidatedByEmployee(validatedByEmployee); + extraHours.setValidationDate(validationDate); + } else if (validation_status == 4) { + extraHours.setRefusedByEmployee(validatedByEmployee); + extraHours.setRefusalDate(validationDate); + } + + + // Save the ExtraHours entity + extraHoursRepo.save(extraHours); + + // Get Daily report + DailyReport dailyReport = + dailyReportRepo + .all() + .filter("self.employee = :employee and self.reportDate = :reportDate") + .bind("employee", employee) + .bind("reportDate", requisitionDate) + .fetchOne(); + + if (dailyReport != null) { + // Get the existing list of ExtraHours + List supHoursList = dailyReport.getSupHoursList(); + + // If the list is null, initialize it + if (supHoursList == null) { + supHoursList = new ArrayList<>(); + } + + // Add the new ExtraHours to the list + supHoursList.add(extraHours); + if (validation_status == 3) + dailyReport.setIsValidSupHours(true); + // Set the updated list back to dailyReport + dailyReport.setSupHoursList(supHoursList); + } + } + } catch (JSONException e) { + System.err.println("Failed to parse JSON: " + jsonObject.toString()); + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/leave/LeaveService.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/leave/LeaveService.java index 0b71f12..efdb4f8 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/leave/LeaveService.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/leave/LeaveService.java @@ -31,9 +31,13 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import javax.mail.MessagingException; +import wslite.json.JSONObject; public interface LeaveService { + @Transactional(rollbackOn = {Exception.class}) + public void saveCR(JSONObject jsonObject) throws AxelorException; + public BigDecimal computeDuration(LeaveRequest leave) throws AxelorException; public BigDecimal computeDuration(LeaveRequest leave, LocalDate fromDate, LocalDate toDate) diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/leave/LeaveServiceImpl.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/leave/LeaveServiceImpl.java index c8846ab..9fccf8c 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/leave/LeaveServiceImpl.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/service/leave/LeaveServiceImpl.java @@ -22,6 +22,7 @@ import com.axelor.apps.base.db.DayPlanning; import com.axelor.apps.base.db.EventsPlanning; import com.axelor.apps.base.db.ICalendarEvent; import com.axelor.apps.base.db.WeeklyPlanning; +import com.axelor.apps.base.db.repo.CompanyRepository; import com.axelor.apps.base.db.repo.ICalendarEventRepository; import com.axelor.apps.base.ical.ICalendarService; import com.axelor.apps.base.service.app.AppBaseService; @@ -31,6 +32,9 @@ import com.axelor.apps.hr.db.HRConfig; import com.axelor.apps.hr.db.LeaveLine; import com.axelor.apps.hr.db.LeaveReason; import com.axelor.apps.hr.db.LeaveRequest; +import com.axelor.apps.hr.db.DailyReport; +import com.axelor.apps.hr.db.repo.EmployeeRepository; +import com.axelor.apps.hr.db.repo.DailyReportRepository; import com.axelor.apps.hr.db.repo.LeaveLineRepository; import com.axelor.apps.hr.db.repo.LeaveReasonRepository; import com.axelor.apps.hr.db.repo.LeaveRequestRepository; @@ -55,6 +59,12 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; import javax.mail.MessagingException; +import wslite.json.JSONException; +import wslite.json.JSONObject; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + public class LeaveServiceImpl implements LeaveService { @@ -67,10 +77,16 @@ public class LeaveServiceImpl implements LeaveService { protected TemplateMessageService templateMessageService; protected ICalendarEventRepository icalEventRepo; protected ICalendarService icalendarService; + protected CompanyRepository companyRepo; + protected DailyReportRepository dailyReportRepo; + protected EmployeeRepository employeeRepo; @Inject public LeaveServiceImpl( LeaveLineRepository leaveLineRepo, + CompanyRepository companyRepo, + DailyReportRepository dailyReportRepo, + EmployeeRepository employeeRepo, WeeklyPlanningService weeklyPlanningService, PublicHolidayHrService publicHolidayHrService, LeaveRequestRepository leaveRequestRepo, @@ -89,6 +105,9 @@ public class LeaveServiceImpl implements LeaveService { this.templateMessageService = templateMessageService; this.icalEventRepo = icalEventRepo; this.icalendarService = icalendarService; + this.companyRepo = companyRepo; + this.employeeRepo = employeeRepo; + this.dailyReportRepo = dailyReportRepo; } /** @@ -913,4 +932,170 @@ public class LeaveServiceImpl implements LeaveService { } return null; } + + @Transactional(rollbackOn = {Exception.class}) + public void saveCR(JSONObject jsonObject) throws AxelorException { + Company company = companyRepo.all().filter("self.id = 1").fetchOne(); + 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 dateDebut = jsonObject.getString("de"); + String dateFin = jsonObject.getString("a"); + String commentaire = jsonObject.getString("commentaire"); + 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 Dates + LocalDate debutDate; + try { + debutDate = LocalDate.parse(dateDebut); + } catch (DateTimeParseException e) { + System.out.println("Error parsing dateDebut: " + dateDebut); + debutDate = null; + } + + LocalDate finDate; + try { + finDate = LocalDate.parse(dateFin); + } catch (DateTimeParseException e) { + System.out.println("Error parsing dateFin: " + dateFin); + finDate = null; + } + + LocalDateTime debutDateTime = debutDate.atStartOfDay(); + LocalDateTime finDateTime = finDate.atStartOfDay(); + + LeaveRequest leaveRequest = leaveRequestRepo + .all() + .filter("self.ticketId = :ticketId") + .bind("ticketId", idInt) + .fetchOne(); + + if (leaveRequest != null) { + // Authorization exists, compare previous and new status + int previousStatus = leaveRequest.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 + leaveRequest.setValidatedByEmployee(validatedByEmployee); + leaveRequest.setValidationDate(validationDate); + leaveRequest.setStatusSelect(newStatus); + // Save the updated Authorization + leaveRequestRepo.save(leaveRequest); + // Get Daily report + List dailyReports = dailyReportRepo + .all() + .filter("self.employee = :employee and self.reportDate >= :debutDate and self.reportDate <= :finDate") + .bind("employee", employee) + .bind("debutDate", debutDate) + .bind("finDate", finDate) + .fetch(); + + if (dailyReports != null) { + for (DailyReport dailyReport : dailyReports) { + dailyReport.setIsAuthorizedAbsence(true); + dailyReportRepo.save(dailyReport); + } + } + } else if (previousStatus == 2 && newStatus == 4) { + System.out.println("Tickets :" + idInt + " Status changed from " + previousStatus + " to " + newStatus); + leaveRequest.setRefusedByEmployee(validatedByEmployee); + leaveRequest.setRefusalDate(validationDate); + leaveRequest.setStatusSelect(newStatus); + // Save the updated Authorization + leaveRequestRepo.save(leaveRequest); + } + } else { + // Create an instance of ExtraHours + leaveRequest = new LeaveRequest(); + leaveRequest.setEmployee(employee); + leaveRequest.setTicketId(idInt); + leaveRequest.setTicket(ticketLink); + leaveRequest.setFromDateT(debutDateTime); + leaveRequest.setToDateT(finDateTime); + leaveRequest.setDescription(commentaire); + leaveRequest.setStatusSelect(validation_status); + leaveRequest.setCompany(company); + + if (validation_status == 3) { + leaveRequest.setValidatedByEmployee(validatedByEmployee); + leaveRequest.setValidationDate(validationDate); + } else if (validation_status == 4) { + leaveRequest.setRefusedByEmployee(validatedByEmployee); + leaveRequest.setRefusalDate(validationDate); + } + + // Save the ExtraHours entity + leaveRequestRepo.save(leaveRequest); + + // Get Daily report + List dailyReports = dailyReportRepo + .all() + .filter("self.employee = :employee and self.reportDate >= :debutDate and self.reportDate <= :finDate") + .bind("employee", employee) + .bind("debutDate", debutDate) + .bind("finDate", finDate) + .fetch(); + + if (dailyReports != null) { + for (DailyReport dailyReport : dailyReports) { + dailyReport.setLeaveRequest(leaveRequest); // Set the recup leave for each report + if(validation_status == 3) + dailyReport.setIsAuthorizedAbsence(true); + dailyReportRepo.save(dailyReport); + } + } + } + } catch (JSONException e) { + System.err.println("Failed to parse JSON: " + jsonObject.toString()); + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } 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 new file mode 100644 index 0000000..e45f96e --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AbsenceController.java @@ -0,0 +1,117 @@ +package com.axelor.apps.hr.web; + +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.repo.AbsenceRepository; +import com.axelor.apps.hr.db.repo.DailyReportRepository; +import com.axelor.apps.hr.service.AbsenceServiceImpl; +import com.axelor.inject.Beans; +import com.axelor.rpc.ActionRequest; +import com.axelor.rpc.ActionResponse; +import java.math.BigDecimal; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; + +public class AbsenceController { + + public void attachTheAbsenceWithDailyReport(ActionRequest request, ActionResponse response) { + try { + Long absenceId = (Long) request.getContext().asType(Absence.class).getId(); + Absence absence = Beans.get(AbsenceRepository.class).find(absenceId); + + LocalDate absenceStartDate = absence.getStartDate().toLocalDate(); + 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(); + + // Check if there are any reports + if (dailyreports.isEmpty()) { + return; + } + + Beans.get(AbsenceServiceImpl.class).attachTheAbsenceWithDailyReport(absence, dailyreports); + response.setReload(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void chooseAbsenceType(ActionRequest request, ActionResponse response) { + try { + // Get the selected absence type from the request parameters + String selectedType = (String) request.getContext().get("selectedType"); + + // Get the list of selected absence IDs + List absenceIds = (List) request.getContext().get("_ids"); + + // Iterate over the selected absence IDs + for (Long absenceId : absenceIds) { + // Find the absence entity by ID + Absence absence = Beans.get(AbsenceRepository.class).find(absenceId); + + if (absence != null) { + // Call the chooseAbsenceType method of the service with both absence and selectedType + Beans.get(AbsenceServiceImpl.class).chooseAbsenceType(absence, selectedType); + } else { + // Log a warning or handle the absence being null appropriately + System.err.println("Absence entity with ID " + absenceId + " not found."); + } + } + + // Set reload to true to refresh the view + response.setReload(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void archiveAbsence(ActionRequest request, ActionResponse response) { + try { + Long absenceId = (Long) request.getContext().asType(Absence.class).getId(); + Absence absence = Beans.get(AbsenceRepository.class).find(absenceId); + + List dailyreports = Beans.get(DailyReportRepository.class) + .all() + .filter("self.absence = :absence") + .bind("absence", absenceId) + .fetch(); + + Beans.get(AbsenceServiceImpl.class).deAttachTheAbsenceWithDailyReport(absence, dailyreports); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public void calculateTotalAbsenceHours(ActionRequest request, ActionResponse response) { + try { + Absence absence = request.getContext().asType(Absence.class); + // Absence absence = Beans.get(AbsenceRepository.class).find(absenceId); + + LocalDateTime absenceStartDate = absence.getStartDate(); + LocalDateTime absenceEndDate = absence.getEndDate(); + if (absenceStartDate.isAfter(absenceEndDate)) { + response.setAlert("Start date cannot be after end date."); + } + else { + BigDecimal totalAbsenceHours = Beans.get(AbsenceServiceImpl.class).calculateTotalAbsenceHours(absenceStartDate,absenceEndDate); + response.setValue("totalAbsenceHours", totalAbsenceHours); + } + } 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 new file mode 100644 index 0000000..e4688a1 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/AuthorizationController.java @@ -0,0 +1,286 @@ +package com.axelor.apps.hr.web; + +import com.axelor.app.AppSettings; +import com.axelor.apps.base.web.AppBaseController; +import com.axelor.apps.hr.service.AuthorizationService; +import com.axelor.inject.Beans; +import com.axelor.rpc.ActionRequest; +import com.axelor.rpc.ActionResponse; +import com.google.inject.Singleton; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.invoke.MethodHandles; +import java.net.HttpURLConnection; +import java.net.URL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import wslite.json.JSONArray; +import wslite.json.JSONException; +import wslite.json.JSONObject; + +@Singleton +public class AuthorizationController { + + private final AppSettings appSettings = AppSettings.get(); + private final String BASE_URL = appSettings.get("portail.api.baseurl"); + + private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + public void fetchAuthorization(ActionRequest request, ActionResponse response) { + try { + + String accessToken = AppBaseController.getAccessToken(); + if (accessToken == null) { + logger.error("Access token is null, unable to proceed."); + return; + } + + String jsonResponse = fetchAA(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).saveAA(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 fetchAA."); + } + } catch (Exception e) { + // General catch for unexpected exceptions + System.err.println( + "An error occurred while fetching Authorization Absence: " + e.getMessage()); + e.printStackTrace(); + } + } + + public void fetchEntryAuthorization(ActionRequest request, ActionResponse response) { + try { + String accessToken = AppBaseController.getAccessToken(); + if (accessToken == null) { + logger.error("Access token is null, unable to proceed."); + return; + } + String jsonResponse = fetchAE(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).saveAE(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 fetchAE."); + } + } catch (Exception e) { + // General catch for unexpected exceptions + System.err.println("An error occurred while fetching Entry Authorization: " + e.getMessage()); + e.printStackTrace(); + } + } + + public void fetchExitAuthorization(ActionRequest request, ActionResponse response) { + try { + String accessToken = AppBaseController.getAccessToken(); + if (accessToken == null) { + logger.error("Access token is null, unable to proceed."); + return; + } + String jsonResponse = fetchBS(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).saveBS(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 fetchBS."); + } + } catch (Exception e) { + // General catch for unexpected exceptions + System.err.println("An error occurred while fetching Entry Authorization: " + e.getMessage()); + e.printStackTrace(); + } + } + + public String fetchAA(String accessToken) { + String endpoint = BASE_URL + "tickets/aa/"; + 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 + } + } + } + + public String fetchAE(String accessToken) { + String endpoint = BASE_URL + "tickets/ae/"; + 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 + } + } + } + + public String fetchBS(String accessToken) { + String endpoint = BASE_URL + "tickets/bs/"; + 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 new file mode 100644 index 0000000..4ed42cb --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/DailyReportController.java @@ -0,0 +1,118 @@ +package com.axelor.apps.hr.web; + +import com.axelor.apps.hr.db.Shift; +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.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.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.time.LocalDateTime; +import java.time.LocalTime; + +public class DailyReportController { + + private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + /** + * @param request + * @param response + */ + + public void workingHours(ActionRequest request, ActionResponse response) { + try { + Long dailyReportId = (Long) request.getContext().asType(DailyReport.class).getId(); + DailyReport DailyReport = Beans.get(DailyReportRepository.class).find(dailyReportId); + if (DailyReport == null) { + throw new IllegalArgumentException( + "DailyReport with ID " + dailyReportId + " not found."); + } + Beans.get(DailyReportServiceImpl.class).determineShift(DailyReport); + Beans.get(DailyReportServiceImpl.class).workingHours(DailyReport); + response.setReload(true); + } catch (Exception e) { + System.out.println("\n\n\n\n\n\n\n\n"); + e.printStackTrace(); + } + } + + public void workingHours1(ActionRequest request, ActionResponse response) { + try { + Long dailyReportId = (Long) request.getContext().asType(DailyReport.class).getId(); + DailyReport DailyReport = Beans.get(DailyReportRepository.class).find(dailyReportId); + if (DailyReport == null) { + throw new IllegalArgumentException( + "DailyReport with ID " + dailyReportId + " not found."); + } + Beans.get(DailyReportServiceImpl.class).workingHours(DailyReport); + response.setReload(true); + } catch (Exception e) { + System.out.println("\n\n\n\n\n\n\n\n"); + e.printStackTrace(); + } + } + + public void workingHoursForAll(ActionRequest request, ActionResponse response) { + try { + List dailyReportList = + Beans.get(DailyReportRepository.class) + .all() + .filter("self.isCalculated = ?", false) + .fetch(); + + if (!dailyReportList.isEmpty()) { + Beans.get(DailyReportServiceImpl.class).workingHoursForAll(dailyReportList); + response.setFlash("Working hours calculated successfully."); + } else { + response.setFlash("No new records to process."); + } + + response.setReload(true); + } catch (Exception e) { + log.error("An error occurred while calculating working hours.", e); + response.setError("An error occurred while calculating working hours."); + } + } + + public void workingHoursForSelectedInstances(ActionRequest request, ActionResponse response) { + List dailyReportIds = (List) request.getContext().get("_ids"); + if (dailyReportIds != null && !dailyReportIds.isEmpty()) { + + List dailyReportList = + Beans.get(DailyReportRepository.class) + .all() + .filter( + "self.id in ?1 ",dailyReportIds) + .fetch(); + + for(DailyReport dailyReport: dailyReportList){ + Beans.get(DailyReportServiceImpl.class).determineShift(dailyReport); + } + Beans.get(DailyReportServiceImpl.class).workingHoursForAll(dailyReportList); + response.setFlash("Working hours calculated successfully."); + } else { + response.setFlash("No Daily report selected"); + } + } + + public void workingHoursForChangedInstances(ActionRequest request, ActionResponse response) { + List dailyReportList = + Beans.get(DailyReportRepository.class) + .all() + .filter("self.isCalculated = ? and self.isChanged = ?", false,true) + .fetch(); + + for(DailyReport dailyReport: dailyReportList){ + Beans.get(DailyReportServiceImpl.class).determineShift(dailyReport); + } + + Beans.get(DailyReportServiceImpl.class).workingHoursForAll(dailyReportList); + response.setReload(true); + response.setFlash("Working hours calculated successfully."); + } +} 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 2a7d077..3346388 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 @@ -17,6 +17,8 @@ */ package com.axelor.apps.hr.web; +import com.axelor.apps.hr.db.Granding; +import com.axelor.apps.hr.db.repo.GrandingRepository; import com.axelor.apps.ReportFactory; import com.axelor.apps.base.db.Partner; import com.axelor.apps.hr.db.DPAE; @@ -42,6 +44,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import wslite.json.JSONException; import wslite.json.JSONObject; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; @Singleton public class EmployeeController { @@ -141,4 +146,81 @@ public class EmployeeController { response.setReload(true); } + public void enrollEmployee(ActionRequest request, ActionResponse response){ + Long employeeId = (Long) request.getContext().asType(Employee.class).getId(); + Employee employee = Beans.get(EmployeeRepository.class).find(employeeId); + + Granding granding = Beans.get(GrandingRepository.class) + .all() + .filter("self.name = 'POINTEUSE-RDC'") + .fetchOne(); + + String ipAdress = granding.getIpAdress(); + String code = granding.getCode().toString(); + + if(employee.getIsEnrolled()) { + response.setFlash("Employee is already enrolled."); + } else { + if (employee.getContactPartner() != null){ + String employeeRegistrationNumber = employee.getRegistrationNumber(); + String employeeName = employee.getContactPartner().getName(); + + try { + + String[] args = { + "python", + "C:\\Users\\administrator\\Desktop\\attendance\\main.py", + "--commande", "create", + "--ip_address", ipAdress, + "--code", code, + "--user_id", employeeRegistrationNumber, + "--name", employeeName + }; + + Process p = Runtime.getRuntime().exec(args); + + // Capture the output stream (standard output) + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + LOG.info("Python script (Employee Enrolling) output: " + line); + } + + // Capture the error stream (standard error) + BufferedReader errorReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + while ((line = errorReader.readLine()) != null) { + LOG.error("Python script (Employee Enrolling) error: " + line); + } + + // Wait for the process to complete and check the exit value + int exitCode = p.waitFor(); + + // Check if the process ran successfully + if (exitCode == 0) { + LOG.info("Python script executed successfully (Employee Enrolling)."); + response.setFlash("Employee enrolled successfully."); + } else { + LOG.error("Python script execution (Employee Enrolling) failed with exit code: " + exitCode); + response.setFlash("Failed to enroll the Employee."); + } + + } catch (IOException e) { + // Check if the file is not found based on the message or error code + if (e.getMessage().contains("The system cannot find the file specified")) { + LOG.error("Python script file (Employee Enrolling) not found: " + e.getMessage()); + } else { + LOG.error("An error occurred while executing the Python script (Employee Enrolling).", e); + } + response.setFlash("Failed to enroll the Employee."); + TraceBackService.trace(e); + } catch (InterruptedException e) { + // Handle any interruption errors + response.setFlash("Failed to enroll the Employee."); + LOG.error("Python script (Employee Enrolling) execution interrupted.", e); + Thread.currentThread().interrupt(); + } + } + } + } + } diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/GrandingController.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/GrandingController.java new file mode 100644 index 0000000..5c358b8 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/GrandingController.java @@ -0,0 +1,86 @@ +package com.axelor.apps.hr.web; + + +import com.axelor.apps.hr.db.Granding; +import com.axelor.apps.hr.db.repo.GrandingRepository; +import com.axelor.inject.Beans; +import com.axelor.rpc.ActionRequest; +import com.axelor.rpc.ActionResponse; +import java.time.temporal.TemporalAdjusters; +import java.util.LinkedHashMap; +import java.util.stream.Collectors; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.lang.invoke.MethodHandles; +import com.axelor.exception.service.TraceBackService; +import java.io.IOException; + + +public class GrandingController { + + private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + public void testGrandingConnection(ActionRequest request, ActionResponse response) { + + Long grandingId = (Long) request.getContext().asType(Granding.class).getId(); + Granding granding = Beans.get(GrandingRepository.class).find(grandingId); + + String ipAdress = granding.getIpAdress(); + String code = granding.getCode().toString(); + + try { + + String[] args = { + "python", + "C:\\Users\\administrator\\Desktop\\attendance\\main.py", + "--commande", "ping", + "--ip_address", ipAdress, + "--code", code + }; + + Process p = Runtime.getRuntime().exec(args); + + // Capture the output stream (standard output) + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + log.info("Python script (Granding ping) output: " + line); + } + + // Capture the error stream (standard error) + BufferedReader errorReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + while ((line = errorReader.readLine()) != null) { + log.error("Python script (Granding ping) error: " + line); + } + + // Wait for the process to complete and check the exit value + int exitCode = p.waitFor(); + + // Check if the process ran successfully + if (exitCode == 0) { + log.info("Python script executed successfully (Granding ping)."); + response.setFlash("Connection successful."); + } else { + log.error("Python script execution (Granding ping) failed with exit code: " + exitCode); + response.setFlash("Connection failed."); + } + + } catch (IOException e) { + // Check if the file is not found based on the message or error code + if (e.getMessage().contains("The system cannot find the file specified")) { + log.error("Python script file (Granding ping) not found: " + e.getMessage()); + } else { + log.error("An error occurred while executing the Python script (Granding ping).", e); + } + response.setFlash("Connection failed."); + TraceBackService.trace(e); + } catch (InterruptedException e) { + // Handle any interruption errors + response.setFlash("Connection failed."); + log.error("Python script (Granding ping) execution interrupted.", e); + Thread.currentThread().interrupt(); + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..4c336d1 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/MonthlyReportController.java @@ -0,0 +1,223 @@ +package com.axelor.apps.hr.web; + +import com.axelor.apps.base.db.Period; +import com.axelor.apps.base.db.repo.PeriodRepository; +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.axelor.apps.hr.db.repo.AbsenceRepository; +import com.axelor.apps.hr.db.repo.EmployeeRepository; +import com.axelor.apps.hr.db.repo.MonthlyReportRepository; +import com.axelor.apps.hr.db.repo.DailyReportRepository; +import com.axelor.apps.hr.service.MonthlyReportServiceImpl; +import com.axelor.inject.Beans; +import com.axelor.rpc.ActionRequest; +import com.axelor.rpc.ActionResponse; +import java.time.DayOfWeek; +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.util.List; + +public class MonthlyReportController { + + public void createMensuelReport(ActionRequest request, ActionResponse response) { + try { + + Object object = request.getContext().get("period"); + LinkedHashMap periodMap = (LinkedHashMap) object; + Integer periodIdInt = (Integer) periodMap.get("id"); + Long periodId = periodIdInt.longValue(); + Period period = Beans.get(PeriodRepository.class).find(periodId); + LocalDate startDate = period.getFromDate(); + LocalDate endDate = period.getToDate(); + + //int remainingDateToLastOfMonth = calculateDaysExcludingFridaysAndThursdays(endDate); + + // Get active employees in this period + List employees = + Beans.get(EmployeeRepository.class) + .all() + .filter("self.quitDate is null or self.quitDate >= :startDate") + .bind("startDate", startDate) + .fetch(); + + // Fetch all rapport journaliers within the date range for all employees + List allDailyReports = + Beans.get(DailyReportRepository.class) + .all() + .filter("self.reportDate >= :startDate and self.reportDate <= :endDate") + .bind("startDate", startDate) + .bind("endDate", endDate) + .fetch(); + + // Fetch all absences within the date range for all employees + List allAbsences = + Beans.get(AbsenceRepository.class) + .all() + .filter("DATE(self.startDate) >= :startDate and DATE(self.startDate) <= :endDate") + .bind("startDate", startDate) + .bind("endDate", endDate) + .fetch(); + + // 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()); + + // 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()); + + if (!employeeDailyReports.isEmpty() || !employeeAbsences.isEmpty()) { + System.out.println("Calculé pour: "+ employee.getRegistrationNumber()); + // Process the employee's monthly report using filtered rapport and absences + Beans.get(MonthlyReportServiceImpl.class) + .createMensuelReport( + employee, + period, + startDate, + endDate, + employeeDailyReports, + employeeAbsences); + } + } + // Indicate that the action was successful and a reload is needed + response.setReload(true); + } catch (Exception e) { + // Handle any exceptions + e.printStackTrace(); + response.setError("An error occurred: " + e.getMessage()); + } + } + + public void updateMensuelReport(ActionRequest request, ActionResponse response) { + try { + + Object periodObject = request.getContext().get("period"); + LinkedHashMap periodMap = (LinkedHashMap) periodObject; + Integer periodIdInt = (Integer) periodMap.get("id"); + Long periodId = periodIdInt.longValue(); + Period period = Beans.get(PeriodRepository.class).find(periodId); + LocalDate startDate = period.getFromDate(); + LocalDate endDate = period.getToDate(); + + // + Object employeesObject = request.getContext().get("employees"); + System.out.println("Employees" + employeesObject); + + // Initialize the employees list + List employees = new ArrayList<>(); + + // Check if employeesObject is not null and cast it to a list + if (employeesObject instanceof List) { + List> employeesList = (List>) employeesObject; + + // Loop through each employee in the list + for (LinkedHashMap employeeMap : employeesList) { + Integer employeeIdInt = (Integer) employeeMap.get("id"); + + // Check for null ID to avoid potential NullPointerException + if (employeeIdInt != null) { + Long employeeId = employeeIdInt.longValue(); + + // Retrieve the employee from the repository + Employee employee = Beans.get(EmployeeRepository.class).find(employeeId); + + if (employee != null) { + employees.add(employee); // Use add() instead of append() + } else { + System.out.println("Employee with ID " + employeeId + " not found."); + } + } else { + System.out.println("Employee ID is missing in the employeeMap."); + } + } + } else { + response.setFlash("No employees Selected."); + } + + //int remainingDateToLastOfMonth = calculateDaysExcludingFridaysAndThursdays(endDate); + List employeesIds = new ArrayList<>(); + for(Employee e:employees){ + employeesIds.add(e.getId().intValue()); + } + + // Fetch all rapport journaliers within the date range for all employees + List allDailyReports = + Beans.get(DailyReportRepository.class) + .all() + .filter("self.employee in :employeesIds and self.reportDate >= :startDate and self.reportDate <= :endDate") + .bind("startDate", startDate) + .bind("endDate", endDate) + .bind("employeesIds", employeesIds) + .fetch(); + + // Fetch all absences within the date range for all employees + List allAbsences = + Beans.get(AbsenceRepository.class) + .all() + .filter("self.employee in :employeesIds and DATE(self.startDate) >= :startDate and DATE(self.startDate) <= :endDate") + .bind("startDate", startDate) + .bind("endDate", endDate) + .bind("employeesIds", employeesIds) + .fetch(); + + // Indicate that the action was successful and a reload is needed + response.setReload(true); + } catch (Exception e) { + // Handle any exceptions + e.printStackTrace(); + response.setError("An error occurred: " + e.getMessage()); + } + } + + /*public void calculePrimeAssiduite(ActionRequest request, ActionResponse response) { + try { + Long monthlyReportId = (Long) request.getContext().asType(MonthlyReport.class).getId(); + MonthlyReport monthlyReport = Beans.get(MonthlyReportRepository.class).find(monthlyReportId); + + Beans.get(MonthlyReportServiceImpl.class).calculePrimeAssiduite(monthlyReport); + + // Indicate that the action was successful and a reload is needed + response.setReload(true); + } catch (Exception e) { + // Handle any exceptions + e.printStackTrace(); + response.setError("An error occurred: " + e.getMessage()); + } + }*/ + + private int calculateDaysExcludingFridaysAndThursdays(LocalDate endDate) { + // Get the last day of the month for endDate + LocalDate lastDayOfMonth = endDate.with(TemporalAdjusters.lastDayOfMonth()); + + // Calculate the number of days from endDate to lastDayOfMonth excluding Fridays and Thursdays + int daysToLastDateExcludingFridaysAndThursdays = 0; + LocalDate currentDate = endDate.plusDays(1); // Start from the day after endDate + + while (!currentDate.isAfter(lastDayOfMonth)) { + DayOfWeek dayOfWeek = currentDate.getDayOfWeek(); + if (dayOfWeek != DayOfWeek.FRIDAY && dayOfWeek != DayOfWeek.THURSDAY) { + daysToLastDateExcludingFridaysAndThursdays++; + } + currentDate = currentDate.plusDays(1); // Move to the next day + } + + return daysToLastDateExcludingFridaysAndThursdays; + } +} diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/extra/hours/ExtraHoursController.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/extra/hours/ExtraHoursController.java index f071c08..c878098 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/extra/hours/ExtraHoursController.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/extra/hours/ExtraHoursController.java @@ -17,13 +17,18 @@ */ package com.axelor.apps.hr.web.extra.hours; +import com.axelor.app.AppSettings; import com.axelor.apps.base.db.Company; +import com.axelor.apps.base.db.ImportConfiguration; import com.axelor.apps.base.db.Wizard; +import com.axelor.apps.base.db.repo.ImportConfigurationRepository; import com.axelor.apps.base.service.PeriodService; import com.axelor.apps.base.service.message.MessageServiceBaseImpl; +import com.axelor.apps.base.web.AppBaseController; import com.axelor.apps.hr.db.Employee; import com.axelor.apps.hr.db.ExtraHours; import com.axelor.apps.hr.db.repo.ExtraHoursRepository; +import com.axelor.apps.hr.exception.IExceptionMessage; import com.axelor.apps.hr.service.HRMenuTagService; import com.axelor.apps.hr.service.HRMenuValidateService; import com.axelor.apps.hr.service.extra.hours.ExtraHoursService; @@ -31,6 +36,7 @@ import com.axelor.apps.message.db.Message; import com.axelor.apps.message.db.repo.MessageRepository; import com.axelor.auth.AuthUtils; import com.axelor.auth.db.User; +import com.axelor.csv.script.ImportExtraHoursConfiguration; import com.axelor.db.Query; import com.axelor.exception.AxelorException; import com.axelor.exception.service.TraceBackService; @@ -41,12 +47,27 @@ import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder; import com.axelor.rpc.ActionRequest; import com.axelor.rpc.ActionResponse; import com.google.inject.Singleton; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.invoke.MethodHandles; +import java.net.HttpURLConnection; +import java.net.URL; import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import wslite.json.JSONArray; +import wslite.json.JSONException; +import wslite.json.JSONObject; @Singleton public class ExtraHoursController { + private final AppSettings appSettings = AppSettings.get(); + private final String BASE_URL = appSettings.get("portail.api.baseurl"); + + private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + public void editExtraHours(ActionRequest request, ActionResponse response) { List extraHoursList = Beans.get(ExtraHoursRepository.class) @@ -284,10 +305,124 @@ public class ExtraHoursController { public void compute(ActionRequest request, ActionResponse response) { try { ExtraHours extraHours = request.getContext().asType(ExtraHours.class); - Beans.get(ExtraHoursService.class).compute(extraHours); + // Beans.get(ExtraHoursService.class).compute(extraHours); response.setValue("totalQty", extraHours.getTotalQty()); } catch (Exception e) { TraceBackService.trace(response, e); } } + + public void getExtraHoursImportConfig(ActionRequest request, ActionResponse response) { + + ImportConfiguration extraHoursImportConfig = + Beans.get(ImportConfigurationRepository.class) + .all() + .filter( + "self.bindMetaFile.fileName = ?1", + ImportExtraHoursConfiguration.IMPORT_EXTRA_HOURS_CONFIG) + .fetchOne(); + + logger.debug("ImportConfig for lead: {}", extraHoursImportConfig); + + if (extraHoursImportConfig == null) { + response.setFlash(I18n.get(IExceptionMessage.EXTRA_HOURS_1)); + } else { + response.setView( + ActionView.define(I18n.get(IExceptionMessage.EXTRA_HOURS_2)) + .model("com.axelor.apps.base.db.ImportConfiguration") + .add("form", "import-configuration-form") + .param("popup", "reload") + .param("forceEdit", "true") + .param("popup-save", "false") + .param("show-toolbar", "false") + .context("_showRecord", extraHoursImportConfig.getId().toString()) + .map()); + } + } + + public void fetchExtraHours(ActionRequest request, ActionResponse response) { + try { + String accessToken = AppBaseController.getAccessToken(); + if (accessToken == null) { + logger.error("Access token is null, unable to proceed."); + return; + } + + String jsonResponse = fetchHS(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(ExtraHoursService.class).saveExtraHours(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 fetchHS."); + } + } catch (Exception e) { + // General catch for unexpected exceptions + System.err.println("An error occurred while fetching extra hours: " + e.getMessage()); + e.printStackTrace(); + } + } + + public String fetchHS(String accessToken) { + String endpoint = BASE_URL + "tickets/hs/"; + 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/leave/LeaveController.java b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/leave/LeaveController.java index 7ccf7c9..def2152 100644 --- a/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/leave/LeaveController.java +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/java/com/axelor/apps/hr/web/leave/LeaveController.java @@ -17,10 +17,12 @@ */ package com.axelor.apps.hr.web.leave; +import com.axelor.app.AppSettings; import com.axelor.apps.base.db.Company; import com.axelor.apps.base.db.Wizard; import com.axelor.apps.base.service.PeriodService; import com.axelor.apps.base.service.message.MessageServiceBaseImpl; +import com.axelor.apps.base.web.AppBaseController; import com.axelor.apps.hr.db.Employee; import com.axelor.apps.hr.db.ExtraHours; import com.axelor.apps.hr.db.LeaveLine; @@ -51,10 +53,26 @@ import com.google.inject.Singleton; import com.google.inject.persist.Transactional; import java.util.List; import java.util.Map; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.invoke.MethodHandles; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import wslite.json.JSONArray; +import wslite.json.JSONException; +import wslite.json.JSONObject; @Singleton public class LeaveController { + private final AppSettings appSettings = AppSettings.get(); + private final String BASE_URL = appSettings.get("portail.api.baseurl"); + private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + public void editLeave(ActionRequest request, ActionResponse response) { try { @@ -398,4 +416,91 @@ public class LeaveController { return Beans.get(HRMenuTagService.class) .countRecordsTag(LeaveRequest.class, LeaveRequestRepository.STATUS_AWAITING_VALIDATION); } + + public void fetchRecupLeave(ActionRequest request, ActionResponse response) { + try { + String accessToken = AppBaseController.getAccessToken(); + if (accessToken == null) { + logger.error("Access token is null, unable to proceed."); + return; + } + + String jsonResponse = fetchCR(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(LeaveService.class).saveCR(jsonObject); + } + + // Reload the response after successfully saving extra hours + response.setReload(true); + + } catch (JSONException e) { + // Log the specific JSON parsing error + e.printStackTrace(); + } + } else { + System.err.println("No response received from fetchCR."); + } + } catch (Exception e) { + // General catch for unexpected exceptions + System.err.println("An error occurred while fetching congé recup: " + e.getMessage()); + e.printStackTrace(); + } + } + + public String fetchCR(String accessToken) { + String endpoint = BASE_URL + "tickets/cr/"; + 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 + System.out.println("HR CR Tickets Response: " + response.toString()); + 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/resources/domains/Absence.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Absence.xml new file mode 100644 index 0000000..7d27ada --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Absence.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Authorization.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Authorization.xml new file mode 100644 index 0000000..ee13914 --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/Authorization.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/CheckInOut.xml b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/CheckInOut.xml new file mode 100644 index 0000000..606a5fa --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/CheckInOut.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ 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 new file mode 100644 index 0000000..1dbb4fe --- /dev/null +++ b/modules/axelor-open-suite/axelor-human-resource/src/main/resources/domains/DailyReport.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 d4c0385..e4922b8 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 @@ -81,6 +81,18 @@ public static final String SEX_M = "M"; ]]> + + + + + + + + + + + + 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 0e16ef6..919c7d2 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 @@ -14,11 +14,22 @@ - - + + +