First commit waiting for Budget Alert

This commit is contained in:
2025-09-04 13:37:35 +01:00
commit 2d681f27f5
4563 changed files with 1061534 additions and 0 deletions

View File

@ -0,0 +1,26 @@
apply plugin: "com.axelor.app-module"
apply from: "../version.gradle"
apply {
version = openSuiteVersion
}
axelor {
title "Axelor Human Resource"
description "Axelor Human Resource Module"
}
dependencies {
compile project(":modules:axelor-project")
compile project(":modules:axelor-bank-payment")
}
task copyWebapp(type: Copy) {
destinationDir = file(rootProject.buildDir)
into("webapp/hr") {
from "src/main/webapp"
}
}
rootProject.tasks.war.dependsOn copyWebapp

View File

@ -0,0 +1,64 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, Object> 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);
}
}

View File

@ -0,0 +1,59 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, Object> 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;
}
}

View File

@ -0,0 +1,92 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.repo.PartnerBaseRepository;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.auth.db.User;
import com.axelor.inject.Beans;
public class EmployeeHRRepository extends EmployeeRepository {
@Override
public Employee save(Employee entity) {
Partner partner = entity.getContactPartner();
if (!partner.getIsContact() && partner.getPartnerTypeSelect() == 0) {
partner.setIsContact(true);
partner.setIsEmployee(true);
Beans.get(PartnerHRRepository.class).save(partner);
}
EmploymentContract employmentContract = entity.getMainEmploymentContract();
if (employmentContract != null && employmentContract.getEmployee() == null) {
employmentContract.setEmployee(entity);
}
return super.save(entity);
}
@Override
public Employee copy(Employee entity, boolean deep) {
entity.setContactPartner(null);
entity.setFixedProPhone(null);
entity.setMobileProPhone(null);
entity.setPhoneAtCustomer(null);
entity.setEmergencyContact(null);
entity.setEmergencyNumber(null);
entity.setDateOfHire(null);
entity.setSeniorityDate(null);
entity.setProfitSharingBeneficiary(null);
entity.setMainEmploymentContract(null);
entity.setExportCode(null);
entity.setEmploymentContractList(null);
entity.setLunchVoucherAdvanceList(null);
entity.setEmployeeAdvanceList(null);
entity.setKilometricLogList(null);
entity.setLeaveLineList(null);
Employee copy = super.copy(entity, deep);
return copy;
}
@Override
public void remove(Employee employee) {
if (employee.getUser() != null) {
UserHRRepository userRepo = Beans.get(UserHRRepository.class);
User user = userRepo.find(employee.getUser().getId());
if (user != null) {
user.setEmployee(null);
userRepo.save(user);
}
}
if (employee.getContactPartner() != null) {
PartnerBaseRepository partnerRepo = Beans.get(PartnerBaseRepository.class);
Partner partner = partnerRepo.find(employee.getContactPartner().getId());
if (partner != null) {
partner.setEmployee(null);
partnerRepo.save(partner);
}
}
super.remove(employee);
}
}

View File

@ -0,0 +1,40 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.service.expense.ExpenseService;
import com.axelor.inject.Beans;
import javax.persistence.PersistenceException;
public class ExpenseHRRepository extends ExpenseRepository {
@Override
public Expense save(Expense expense) {
try {
expense = super.save(expense);
Beans.get(ExpenseService.class).setDraftSequence(expense);
if (expense.getStatusSelect() == ExpenseRepository.STATUS_DRAFT) {
Beans.get(ExpenseService.class).completeExpenseLines(expense);
}
return expense;
} catch (Exception e) {
throw new PersistenceException(e.getLocalizedMessage());
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.hr.db.HrBatch;
public class HrBatchHRRepository extends HrBatchRepository {
@Override
public HrBatch copy(HrBatch entity, boolean deep) {
HrBatch copy = super.copy(entity, deep);
copy.setBatchList(null);
return copy;
}
}

View File

@ -0,0 +1,47 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.account.db.repo.PartnerAccountRepository;
import com.axelor.apps.account.service.AccountingSituationService;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.service.app.AppService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
public class PartnerHRRepository extends PartnerAccountRepository {
@Inject
public PartnerHRRepository(
AppService appService, AccountingSituationService accountingSituationService) {
super(appService, accountingSituationService);
}
@Override
public void remove(Partner partner) {
if (partner.getEmployee() != null) {
EmployeeHRRepository employeeRepo = Beans.get(EmployeeHRRepository.class);
Employee employee = employeeRepo.find(partner.getEmployee().getId());
if (employee != null) {
employeeRepo.remove(employee);
}
}
super.remove(partner);
}
}

View File

@ -0,0 +1,64 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.hr.service.project.ProjectPlanningTimeService;
import com.axelor.apps.project.db.Project;
import com.axelor.apps.project.db.ProjectPlanningTime;
import com.axelor.apps.project.db.repo.ProjectManagementRepository;
import com.axelor.apps.project.db.repo.ProjectPlanningTimeRepository;
import com.axelor.team.db.TeamTask;
import com.google.inject.Inject;
import java.util.List;
public class ProjectHRRepository extends ProjectManagementRepository {
@Inject private ProjectPlanningTimeService projectPlanningTimeService;
@Inject private ProjectPlanningTimeRepository planningTimeRepo;
@Override
public Project save(Project project) {
super.save(project);
List<ProjectPlanningTime> projectPlanningTimeList =
planningTimeRepo
.all()
.filter("self.project = ?1 OR self.project.parentProject = ?1", project)
.fetch();
project.setTotalPlannedHrs(projectPlanningTimeService.getProjectPlannedHrs(project));
Project parentProject = project.getParentProject();
if (parentProject != null) {
parentProject.setTotalPlannedHrs(
projectPlanningTimeService.getProjectPlannedHrs(parentProject));
}
if (projectPlanningTimeList != null) {
for (ProjectPlanningTime planningTime : projectPlanningTimeList) {
TeamTask task = planningTime.getTask();
if (task != null) {
task.setTotalPlannedHrs(projectPlanningTimeService.getTaskPlannedHrs(task));
}
}
}
return project;
}
}

View File

@ -0,0 +1,72 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.hr.service.project.ProjectPlanningTimeService;
import com.axelor.apps.project.db.Project;
import com.axelor.apps.project.db.ProjectPlanningTime;
import com.axelor.apps.project.db.repo.ProjectPlanningTimeRepository;
import com.axelor.apps.project.db.repo.ProjectRepository;
import com.axelor.team.db.TeamTask;
import com.axelor.team.db.repo.TeamTaskRepository;
import com.google.inject.Inject;
public class ProjectPlanningTimeHRRepository extends ProjectPlanningTimeRepository {
@Inject private ProjectPlanningTimeService planningTimeService;
@Inject private ProjectRepository projectRepo;
@Inject private TeamTaskRepository taskRepo;
@Override
public ProjectPlanningTime save(ProjectPlanningTime projectPlanningTime) {
super.save(projectPlanningTime);
Project project = projectPlanningTime.getProject();
project.setTotalPlannedHrs(planningTimeService.getProjectPlannedHrs(project));
Project parentProject = project.getParentProject();
if (parentProject != null) {
parentProject.setTotalPlannedHrs(planningTimeService.getProjectPlannedHrs(parentProject));
}
TeamTask task = projectPlanningTime.getTask();
if (task != null) {
task.setTotalPlannedHrs(planningTimeService.getTaskPlannedHrs(task));
}
return projectPlanningTime;
}
@Override
public void remove(ProjectPlanningTime projectPlanningTime) {
Project project = projectPlanningTime.getProject();
TeamTask task = projectPlanningTime.getTask();
super.remove(projectPlanningTime);
if (task != null) {
taskRepo.save(task);
} else {
projectRepo.save(project);
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.hr.service.project.ProjectPlanningTimeService;
import com.axelor.apps.project.db.Project;
import com.axelor.apps.project.db.repo.TeamTaskProjectRepository;
import com.axelor.team.db.TeamTask;
import com.google.inject.Inject;
public class TeamTaskHRRepository extends TeamTaskProjectRepository {
@Inject private ProjectPlanningTimeService projectPlanningTimeService;
@Override
public TeamTask save(TeamTask teamTask) {
super.save(teamTask);
teamTask.setTotalPlannedHrs(projectPlanningTimeService.getTaskPlannedHrs(teamTask));
Project project = teamTask.getProject();
project.setTotalPlannedHrs(projectPlanningTimeService.getProjectPlannedHrs(project));
Project parentProject = project.getParentProject();
if (parentProject != null) {
parentProject.setTotalPlannedHrs(
projectPlanningTimeService.getProjectPlannedHrs(parentProject));
}
return teamTask;
}
@Override
public void remove(TeamTask teamTask) {
Project project = teamTask.getProject();
super.remove(teamTask);
project.setTotalPlannedHrs(projectPlanningTimeService.getProjectPlannedHrs(project));
}
@Override
public TeamTask copy(TeamTask entity, boolean deep) {
entity.setTotalPlannedHrs(null);
entity.setTotalRealHrs(null);
entity.setProjectPlanningTimeList(null);
return super.copy(entity, deep);
}
}

View File

@ -0,0 +1,84 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.hr.service.timesheet.TimesheetLineService;
import com.axelor.apps.hr.service.timesheet.TimesheetService;
import com.axelor.apps.project.db.Project;
import com.axelor.apps.project.db.repo.ProjectRepository;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
public class TimesheetHRRepository extends TimesheetRepository {
@Inject private TimesheetService timesheetService;
@Inject private TimesheetLineService timesheetLineService;
@Inject private ProjectRepository projectRepository;
@Override
public Timesheet save(Timesheet timesheet) {
if (timesheet.getTimesheetLineList() != null) {
for (TimesheetLine timesheetLine : timesheet.getTimesheetLineList())
Beans.get(TimesheetLineHRRepository.class).computeFullName(timesheetLine);
}
return super.save(timesheet);
}
@Override
public Map<String, Object> validate(Map<String, Object> json, Map<String, Object> context) {
Map<String, Object> obj = super.validate(json, context);
if (json.get("id") == null) {
Timesheet timesheet = create(json);
if (timesheet.getTimesheetLineList() == null || timesheet.getTimesheetLineList().isEmpty()) {
timesheet.setTimesheetLineList(new ArrayList<TimesheetLine>());
obj.put("timesheetLineList", timesheetService.createDefaultLines(timesheet));
}
}
return obj;
}
@Override
public void remove(Timesheet entity) {
if (entity.getStatusSelect() == TimesheetRepository.STATUS_VALIDATED
&& entity.getTimesheetLineList() != null) {
timesheetService.setTeamTaskTotalRealHrs(entity.getTimesheetLineList(), false);
Map<Project, BigDecimal> projectTimeSpentMap =
timesheetLineService.getProjectTimeSpentMap(entity.getTimesheetLineList());
Iterator<Project> projectIterator = projectTimeSpentMap.keySet().iterator();
while (projectIterator.hasNext()) {
Project project = projectIterator.next();
project.setTimeSpent(project.getTimeSpent().subtract(projectTimeSpentMap.get(project)));
projectRepository.save(project);
}
}
super.remove(entity);
}
}

View File

@ -0,0 +1,45 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.hr.db.TimesheetLine;
import javax.persistence.PersistenceException;
public class TimesheetLineHRRepository extends TimesheetLineRepository {
@Override
public TimesheetLine save(TimesheetLine timesheetLine) {
try {
computeFullName(timesheetLine);
return super.save(timesheetLine);
} catch (Exception e) {
throw new PersistenceException(e.getLocalizedMessage());
}
}
public void computeFullName(TimesheetLine timesheetLine) {
timesheetLine.setFullName(
timesheetLine.getTimesheet().getFullName()
+ " "
+ timesheetLine.getDate()
+ " "
+ timesheetLine.getId());
}
}

View File

@ -0,0 +1,55 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.hr.db.TSTimer;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.hr.service.timesheet.timer.TimesheetTimerService;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
public class TimesheetTimerHRRepository extends TSTimerRepository {
@Inject private TimesheetTimerService tsTimerService;
@Override
public TSTimer save(TSTimer tsTimer) {
if (tsTimer.getStatusSelect() == TSTimerRepository.STATUS_STOP) {
if (tsTimer.getTimesheetLine() != null) updateTimesheetLine(tsTimer);
else {
if (tsTimer.getDuration() > 59) {
tsTimerService.generateTimesheetLine(tsTimer);
}
}
}
return super.save(tsTimer);
}
public void updateTimesheetLine(TSTimer tsTimer) {
TimesheetLine timesheetLine = tsTimer.getTimesheetLine();
timesheetLine.setProject(tsTimer.getProject());
timesheetLine.setProduct(tsTimer.getProduct());
timesheetLine.setHoursDuration(
tsTimerService.convertSecondDurationInHours(tsTimer.getDuration()));
timesheetLine.setComments(tsTimer.getComments());
Beans.get(TimesheetLineRepository.class).save(timesheetLine);
}
}

View File

@ -0,0 +1,39 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.db.repo;
import com.axelor.apps.base.db.repo.UserBaseRepository;
import com.axelor.apps.hr.db.Employee;
import com.axelor.auth.db.User;
import com.axelor.inject.Beans;
public class UserHRRepository extends UserBaseRepository {
@Override
public void remove(User user) {
if (user.getEmployee() != null) {
EmployeeHRRepository employeeRepo = Beans.get(EmployeeHRRepository.class);
Employee employee = employeeRepo.find(user.getEmployee().getId());
if (employee != null) {
employee.setUser(null);
employeeRepo.save(employee);
}
}
super.remove(user);
}
}

View File

@ -0,0 +1,215 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.exception;
/** Interface of Exceptions. Enum all exception of axelor-human-resource. */
public interface IExceptionMessage {
static final String HR_CONFIG = /*$$(*/
"Please configure informations for human resources for the company %s" /*)*/;
static final String HR_CONFIG_EXPENSE_TYPE = /*$$(*/
"Please configure the expense type for kilometric allowance in HR config for the company %s" /*)*/;
static final String HR_CONFIG_SENT_EXPENSE_TEMPLATE = /*$$(*/
"Please configure the sent expense template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_VALIDATED_EXPENSE_TEMPLATE = /*$$(*/
"Please configure the validated expense template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_REFUSED_EXPENSE_TEMPLATE = /*$$(*/
"Please configure the refused expense template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_CANCELED_EXPENSE_TEMPLATE = /*$$(*/
"Please configure the canceled expense template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_SENT_TIMESHEET_TEMPLATE = /*$$(*/
"Please configure the sent timehsheet template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_VALIDATED_TIMESHEET_TEMPLATE = /*$$(*/
"Please configure the validated timehsheet template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_REFUSED_TIMESHEET_TEMPLATE = /*$$(*/
"Please configure the refused timehsheet template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_CANCELED_TIMESHEET_TEMPLATE = /*$$(*/
"Please configure the canceled timehsheet template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_SENT_LEAVE_TEMPLATE = /*$$(*/
"Please configure the sent leave template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_VALIDATED_LEAVE_TEMPLATE = /*$$(*/
"Please configure the validated leave template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_REFUSED_LEAVE_TEMPLATE = /*$$(*/
"Please configure the refused leave template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_CANCELED_LEAVE_TEMPLATE = /*$$(*/
"Please configure the canceled leave template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_SENT_EXTRA_HOURS_TEMPLATE = /*$$(*/
"Please configure the sent extra hours template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_VALIDATED_EXTRA_HOURS_TEMPLATE = /*$$(*/
"Please configure the validated extra hours template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_REFUSED_EXTRA_HOURS_TEMPLATE = /*$$(*/
"Please configure the refused extra hours template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_CANCELED_EXTRA_HOURS_TEMPLATE = /*$$(*/
"Please configure the canceled extra hours template in HR config for the company %s" /*)*/;
static final String HR_CONFIG_LEAVE_REASON = /*$$(*/
"Please configure the unjustified absence reason in HR config for the company %s" /*)*/;
static final String HR_CONFIG_LUNCH_VOUCHER_EXPORT_PATH = /*$$(*/
"Please configure the lunch voucher export path in HR config for the company %s" /*)*/;
static final String HR_CONFIG_NO_EXPENSE_SEQUENCE = /*$$(*/
"Company %s does not have any expense's sequence" /*)*/;
static final String HR_CONFIG_FORMULA_VARIABLE_MISSING = /*$$(*/
"Please configure Formula Variables for human resource for the company %s" /*)*/;
static final String TIMESHEET_FROM_DATE = /*$$(*/ "Please add a start date for generation" /*)*/;
static final String TIMESHEET_TO_DATE = /*$$(*/ "Please add an end date for generation" /*)*/;
static final String TIMESHEET_PRODUCT = /*$$(*/ "Please add a product" /*)*/;
static final String TIMESHEET_EMPLOYEE_DAY_PLANNING = /*$$(*/
"Please add an employee's planning related to user %s" /*)*/;
static final String TIMESHEET_EMPLOYEE_DAILY_WORK_HOURS = /*$$(*/
"Please, enter the number of daily work hours per employee %s" /*)*/;
static final String TIMESHEET_DAILY_WORK_HOURS = /*$$(*/
"Please, configure the number of daily work hours." /*)*/;
static final String TIMESHEET_NULL_FROM_DATE = /*$$(*/ "From date can't be empty" /*)*/;
static final String TIMESHEET_NULL_TO_DATE = /*$$(*/ "To date can't be empty" /*)*/;
static final String TIMESHEET_LINE_NULL_DATE = /*$$(*/
"The date of timesheet line %d can't be empty" /*)*/;
static final String GENERAL_EMPLOYEE_ACTIVITY = /*$$(*/
"Please, enter an activity for the employee %s" /*)*/;
static final String TIMESHEET_EMPLOYEE_PUBLIC_HOLIDAY_EVENTS_PLANNING = /*$$(*/
"Please add an employee's public holiday events planning related to user %s" /*)*/;
static final String TIMESHEET_TIMESHEET_LINE_LIST_IS_EMPTY = /*$$(*/
"Timesheet line list is empty, please add a timesheet line list" /*)*/;
static final String TIMESHEET_HOLIDAY = /*$$(*/ "Holiday" /*)*/;
static final String TIMESHEET_DAY_LEAVE = /*$$(*/ "Day leave" /*)*/;
static final String LEAVE_USER_EMPLOYEE = /*$$(*/
"Please create an employee for the user %s" /*)*/;
static final String LEAVE_LINE = /*$$(*/
"There is no leave line for the employee %s and the reason %s." /*)*/;
static final String LEAVE_ALLOW_NEGATIVE_VALUE_EMPLOYEE = /*$$(*/
"Employee %s is not allowed to take leave in advance." /*)*/;
static final String LEAVE_ALLOW_NEGATIVE_VALUE_REASON = /*$$(*/
"You are not able to take leave in advance for the reason '%s'." /*)*/;
static final String LEAVE_ALLOW_NEGATIVE_ALERT = /*$$(*/
"You now have a negative amount of leave available for the reason %s" /*)*/;
static final String LEAVE_REASON_NO_UNIT = /*$$(*/
"Please, choose unit in leave reason %s." /*)*/;
static final String EMPLOYEE_PLANNING = /*$$(*/ "Please, add a planning for employee : %s" /*)*/;
static final String EMPLOYEE_PUBLIC_HOLIDAY = /*$$(*/
"Please, add a public holiday planning for employee : %s" /*)*/;
static final String EMPLOYEE_CONTRACT_OF_EMPLOYMENT = /*$$(*/
"Please, add a contract of employment for employee : %s" /*)*/;
static final String BATCH_MISSING_FIELD = /*$$(*/
"Leave reason and day number have to be defined" /*)*/;
static final String EMPLOYEE_DOUBLE_LEAVE_MANAGEMENT = /*$$(*/
"The employee %s has multiple %s leave lines" /*)*/;
static final String EMPLOYEE_NO_LEAVE_MANAGEMENT = /*$$(*/
"The employee %s has no %s leave line" /*)*/;
static final String EMPLOYEE_NO_SENIORITY_DATE = /*$$(*/
"The employee %s has no seniority date" /*)*/;
static final String EMPLOYEE_NO_BIRTH_DATE = /*$$(*/ "The employee %s has no birth date" /*)*/;
static final String BATCH_LEAVE_MANAGEMENT_ENDING_0 = /*$$(*/
"Employees' leaves attempted to be computed : %s" /*)*/;
static final String BATCH_LEAVE_MANAGEMENT_ENDING_1 = /*$$(*/
"Employees' leaves successfully computed : %s" /*)*/;
static final String BATCH_LEAVE_MANAGEMENT_ENDING_2 = /*$$(*/
"Employees' leaves failed to be computed due to configuration anomaly : %s" /*)*/;
static final String BATCH_LEAVE_MANAGEMENT_ENDING_3 = /*$$(*/
"Employees' leaves failed to be computed due to missing data : %s" /*)*/;
static final String BATCH_LEAVE_MANAGEMENT_QTY_OUT_OF_BOUNDS = /*$$(*/
"Qty must be lower than %d." /*)*/;
static final String BATCH_SENIORITY_LEAVE_MANAGEMENT_FORMULA = /*$$(*/
"There is an error in a formula" /*)*/;
static final String BATCH_PAYROLL_PREPARATION_GENERATION_RECAP = /*$$(*/
"Payroll preparations attempted to be generated : %s" /*)*/;
static final String BATCH_PAYROLL_PREPARATION_SUCCESS_RECAP = /*$$(*/
"Payroll preparations successfully generated : %s" /*)*/;
static final String BATCH_PAYROLL_PREPARATION_DUPLICATE_RECAP = /*$$(*/
"Payroll preparations failed to be generated due to a duplicate one : %s" /*)*/;
static final String BATCH_PAYROLL_PREPARATION_CONFIGURATION_RECAP = /*$$(*/
"Payroll preparations failed to be generated due to missing data : %s" /*)*/;
static final String BATCH_PAYROLL_PREPARATION_EXPORT_RECAP = /*$$(*/
"Payroll preparations exported : %s" /*)*/;
static final String BATCH_TIMESHEET_MISSING_TEMPLATE = /*$$(*/
"You must choose a template." /*)*/;
static final String BATCH_TIMESHEET_REMINDER_DONE = /*$$(*/ "Employees computed: %d" /*)*/;
static final String BATCH_TIMESHEET_REMINDER_ANOMALY = /*$$(*/
"Employees failed to be computed due to anomaly: %d" /*)*/;
static final String BATCH_CREDIT_TRANSFER_EXPENSE_DONE_SINGULAR = /*$$(*/
"%d expense treated successfully," /*)*/;
static final String BATCH_CREDIT_TRANSFER_EXPENSE_DONE_PLURAL = /*$$(*/
"%d expenses treated successfully," /*)*/;
static final String LUNCH_VOUCHER_MIN_STOCK = /*$$(*/
"Minimum stock of lunch vouchers will be reached for the company %s. Minimum Stock allowed : %s. Available Stock : %s" /*)*/;
static final String KILOMETRIC_LOG_NO_YEAR = /*$$(*/
"There is no year for society %s which includes date %s" /*)*/;
static final String KILOMETRIC_ALLOWANCE_NO_RULE = /*$$(*/
"There is no matching condition for the allowance %s" /*)*/;
static final String KILOMETRIC_ALLOWANCE_NO_DATE_SELECTED = /*$$(*/
"There is no year selected for the allowance." /*)*/;
static final String PAYROLL_PREPARATION_DUPLICATE = /*$$(*/
"There is already a payroll preparation for the employee %s, the company %s and the period %s" /*)*/;
/** Expense service */
static final String EXPENSE_JOURNAL = /*$$(*/
"You must configure an expenses journal(company : %s)" /*)*/;
static final String EXPENSE_ACCOUNT = /*$$(*/
"You must configure an expenses account (company : %s)" /*)*/;
static final String EXPENSE_ACCOUNT_TAX = /*$$(*/
"You must configure an account for expenses taxes (company : %s)" /*)*/;
static final String EXPENSE_CANCEL_MOVE = /*$$(*/
"Move already used, you must unreconcile it first" /*)*/;
static final String EXPENSE_TAX_PRODUCT = /*$$(*/ "No Tax for the product %s" /*)*/;
static final String EXPENSE_MISSING_PERIOD = /*$$(*/ "Please fill the period" /*)*/;
static final String EXPENSE_MISSING_PAYMENT_MODE = /*$$(*/ "Please fill the payment mode." /*)*/;
/** Timesheet Editor */
static final String NEW_PROJECT_LINE = /*$$(*/ "New project line" /*)*/;
/** Kilometric allowance */
String KILOMETRIC_ALLOWANCE_GOOGLE_MAPS_ERROR = /*$$(*/ "Google Maps error: %s" /*)*/;
String KILOMETRIC_ALLOWANCE_OSM_ERROR = /*$$(*/ "Open Street Maps error: %s" /*)*/;
static final String EXPENSE_PAYMENT_CANCEL = /*$$(*/
"The bank order linked to this expense has already been carried out/rejected, and thus can't be canceled" /*)*/;
/** Kilometric service */
static final String KILOMETRIC_ALLOWANCE_RATE_MISSING = /*$$(*/
"The kilometric allowance rate corresponding to the kilometric allow param %s and the company %s is missing" /*)*/;
/** TsTimer Service */
String NO_TIMESHEET_CREATED = /*$$(*/
"No timesheet line has been created because the duration is less than 1 minute" /*)*/;
static final String EXPENSE_NOT_SELECTED = /*$$(*/ "Please, select an expense" /*)*/;
static final String BATCH_EMPLOYMENT_CONTRACT_EXPORT_RECAP = /*$$(*/
"Employment contracts exported : %s" /*)*/;
static final String UNIT_SELECT_FOR_LEAVE_REASON = /*$$(*/
"Please configure the unit for this type of absence" /*)*/;
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" /*)*/;
}

View File

@ -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<DailyReport> 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;
}
}
}

View File

@ -0,0 +1,92 @@
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;
import com.axelor.app.AppSettings;
public class FetchAttendanceJob implements Job {
private final Logger log = LoggerFactory.getLogger(FetchCheckInOutJob.class);
private final String pythonScriptDir = AppSettings.get().get("services.dir");
@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
if (isRunning(context)) {
return;
}
if (pythonScriptDir == null || pythonScriptDir.isEmpty()) {
log.error("Python script path is not configured in AppSettings.");
return;
}
try {
// Define the command to run the Python script with the correct path (V3)
String[] args = {"python", pythonScriptDir + "\\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;
}
}
}

View File

@ -0,0 +1,107 @@
package com.axelor.apps.hr.job;
import com.axelor.app.AppSettings;
import com.axelor.apps.hr.db.repo.CheckInOutRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDate;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FetchCheckInOutJob implements Job {
private final Logger log = LoggerFactory.getLogger(FetchCheckInOutJob.class);
private final String pythonScriptDir = AppSettings.get().get("services.dir");
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
if (isRunning(context)) {
return;
}
if (pythonScriptDir == null || pythonScriptDir.isEmpty()) {
log.error("Python script path is not configured in AppSettings.");
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", pythonScriptDir + "\\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;
}
}
}

View File

@ -0,0 +1,238 @@
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);
}
});
executorService.submit(
() -> {
try {
log.info("Fetching authorization AP...");
String jsonResponse = Beans.get(AuthorizationController.class).fetchAP(token);
log.info("Fetched authorization AP successfully.");
if (jsonResponse != null) {
try {
JSONArray jsonArray = new JSONArray(jsonResponse);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
Beans.get(AuthorizationService.class).saveAP(jsonObject);
}
} catch (JSONException e) {
log.error("Failed to parse JSON: " + jsonResponse, e);
}
} else {
log.warn("No response received from fetchAP.");
}
} catch (Exception e) {
log.error("Failed to fetch Salary authorization: ", e);
}
});
// Leave requests fetch task
executorService.submit(
() -> {
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;
}
}
}

View File

@ -0,0 +1,678 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.mobile;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmployeeVehicle;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.ExpenseLine;
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.Timesheet;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.hr.db.repo.EmployeeVehicleRepository;
import com.axelor.apps.hr.db.repo.ExpenseLineRepository;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.apps.hr.db.repo.KilometricAllowParamRepository;
import com.axelor.apps.hr.db.repo.LeaveLineRepository;
import com.axelor.apps.hr.db.repo.LeaveReasonRepository;
import com.axelor.apps.hr.db.repo.LeaveRequestRepository;
import com.axelor.apps.hr.db.repo.TimesheetLineRepository;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.KilometricService;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.hr.service.expense.ExpenseService;
import com.axelor.apps.hr.service.leave.LeaveService;
import com.axelor.apps.hr.service.timesheet.TimesheetLineService;
import com.axelor.apps.hr.service.timesheet.TimesheetService;
import com.axelor.apps.project.db.Project;
import com.axelor.apps.project.db.repo.ProjectRepository;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.common.base.Strings;
import com.google.common.io.Files;
import com.google.inject.persist.Transactional;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.math.BigDecimal;
import java.net.URLConnection;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class HumanResourceMobileController {
/**
* This method is used in mobile application. It was in ExpenseController
*
* @param request
* @param response
* @throws AxelorException
* <p>POST
* /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:insertKMExpenses
* Content-Type: application/json
* <p>URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:insertKMExpenses fields:
* kmNumber, locationFrom, locationTo, allowanceTypeSelect, comments, date, projectTask,
* kilometricAllowParam
* <p>payload: { "data": { "action":
* "com.axelor.apps.hr.mobile.HumanResourceMobileController:insertKMExpenses", "kmNumber":
* 350.00, "locationFrom": "Paris", "locationTo": "Marseille", "allowanceTypeSelect": 1,
* "comments": "no", "date": "2018-02-22", "expenseProduct": 43 } }
*/
@Transactional(rollbackOn = {Exception.class})
public void insertKMExpenses(ActionRequest request, ActionResponse response)
throws AxelorException {
User user = AuthUtils.getUser();
if (user != null) {
ExpenseService expenseService = Beans.get(ExpenseService.class);
Expense expense = expenseService.getOrCreateExpense(user);
ExpenseLine expenseLine = new ExpenseLine();
expenseLine.setDistance(new BigDecimal(request.getData().get("kmNumber").toString()));
expenseLine.setFromCity(request.getData().get("locationFrom").toString());
expenseLine.setToCity(request.getData().get("locationTo").toString());
expenseLine.setKilometricTypeSelect(
new Integer(request.getData().get("allowanceTypeSelect").toString()));
expenseLine.setComments(request.getData().get("comments").toString());
expenseLine.setExpenseDate(LocalDate.parse(request.getData().get("date").toString()));
expenseLine.setProject(
Beans.get(ProjectRepository.class)
.find(Long.valueOf(request.getData().get("projectTask").toString())));
HRConfigService hrConfigService = Beans.get(HRConfigService.class);
HRConfig hrConfig = hrConfigService.getHRConfig(expense.getCompany());
Product expenseProduct = hrConfigService.getKilometricExpenseProduct(hrConfig);
expenseLine.setExpenseProduct(expenseProduct);
Employee employee = user.getEmployee();
if (employee != null) {
KilometricAllowParamRepository kilometricAllowParamRepo =
Beans.get(KilometricAllowParamRepository.class);
expenseLine.setKilometricAllowParam(
kilometricAllowParamRepo.find(
Long.valueOf(request.getData().get("kilometricAllowParam").toString())));
expenseLine.setTotalAmount(
Beans.get(KilometricService.class).computeKilometricExpense(expenseLine, employee));
expenseLine.setUntaxedAmount(expenseLine.getTotalAmount());
}
expense.addKilometricExpenseLineListItem(expenseLine);
Beans.get(ExpenseRepository.class).save(expense);
response.setValue("id", expenseLine.getId());
}
}
/**
* This method is used in mobile application. It was in ExpenseController
*
* @param request
* @param response
* @throws AxelorException
* <p>POST
* /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:removeLines
* Content-Type: application/json
* <p>URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:removeLines no field
* <p>payload: { "data": { "action":
* "com.axelor.apps.hr.mobile.HumanResourceMobileController:removeLines" } }
*/
@Transactional
public void removeLines(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
try {
if (user == null) {
return;
}
Expense expense =
Beans.get(ExpenseRepository.class)
.all()
.filter(
"self.statusSelect = ?1 AND self.user.id = ?2",
ExpenseRepository.STATUS_DRAFT,
user.getId())
.order("-id")
.fetchOne();
if (expense == null) {
return;
}
List<ExpenseLine> expenseLineList =
Beans.get(ExpenseService.class).getExpenseLineList(expense);
if (expenseLineList != null && !expenseLineList.isEmpty()) {
Iterator<ExpenseLine> expenseLineIter = expenseLineList.iterator();
while (expenseLineIter.hasNext()) {
ExpenseLine generalExpenseLine = expenseLineIter.next();
if (generalExpenseLine.getKilometricExpense() != null
&& (expense.getKilometricExpenseLineList() != null
&& !expense.getKilometricExpenseLineList().contains(generalExpenseLine)
|| expense.getKilometricExpenseLineList() == null)) {
expenseLineIter.remove();
}
}
}
response.setValue("expenseLineList", expenseLineList);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/*
* This method is used in mobile application.
* It was in ExpenseServiceImpl
* @param request
* @param response
*
* POST /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:insertOrUpdateExpenseLine
* Content-Type: application/json
*
* URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:insertOrUpdateExpenseLine
* fields: (id,) project, expenseType, date, comments, toInvoice, unTaxTotal, taxTotal, justification
*
* payload:
* { "data": {
* "action": "com.axelor.apps.hr.mobile.HumanResourceMobileController:insertOrUpdateExpenseLine",
* "id": 1,
* "project": 2,
* "expenseType": 10,
* "date": "2018-02-22",
* "comments": "No",
* "toInvoice": "no",
* "unTaxTotal": 100,
* "taxTotal": 2,
* "justification": "no"
* } }
*/
@Transactional
public void insertOrUpdateExpenseLine(ActionRequest request, ActionResponse response) {
try {
User user = AuthUtils.getUser();
Map<String, Object> requestData = request.getData();
Project project =
Beans.get(ProjectRepository.class)
.find(Long.valueOf(requestData.get("project").toString()));
Product product =
Beans.get(ProductRepository.class)
.find(Long.valueOf(requestData.get("expenseType").toString()));
if (user != null) {
ExpenseService expenseService = Beans.get(ExpenseService.class);
Expense expense = expenseService.getOrCreateExpense(user);
ExpenseLine expenseLine;
Object idO = requestData.get("id");
if (idO != null) {
expenseLine = Beans.get(ExpenseLineRepository.class).find(Long.valueOf(idO.toString()));
} else {
expenseLine = new ExpenseLine();
}
expenseLine.setExpenseDate(
LocalDate.parse(requestData.get("date").toString(), DateTimeFormatter.ISO_DATE));
expenseLine.setComments(requestData.get("comments").toString());
expenseLine.setExpenseProduct(product);
expenseLine.setProject(project);
expenseLine.setUser(user);
expenseLine.setTotalAmount(new BigDecimal(requestData.get("unTaxTotal").toString()));
expenseLine.setTotalTax(new BigDecimal(requestData.get("taxTotal").toString()));
expenseLine.setUntaxedAmount(
expenseLine.getTotalAmount().subtract(expenseLine.getTotalTax()));
expenseLine.setToInvoice(new Boolean(requestData.get("toInvoice").toString()));
String justification = (String) requestData.get("justification");
if (!Strings.isNullOrEmpty(justification)) {
String MIME_IMAGE_X_ICON = "image/x-icon";
String MIME_IMAGE_SVG_XML = "image/svg+xml";
String MIME_IMAGE_BMP = "image/bmp";
String MIME_IMAGE_GIF = "image/gif";
String MIME_IMAGE_JPEG = "image/jpeg";
String MIME_IMAGE_TIFF = "image/tiff";
String MIME_IMAGE_PNG = "image/png";
Map<String, String> mimeTypeMapping = new HashMap<>(200);
mimeTypeMapping.put(MIME_IMAGE_X_ICON, "ico");
mimeTypeMapping.put(MIME_IMAGE_SVG_XML, "svg");
mimeTypeMapping.put(MIME_IMAGE_BMP, "bmp");
mimeTypeMapping.put(MIME_IMAGE_GIF, "gif");
mimeTypeMapping.put(MIME_IMAGE_JPEG, "jpg");
mimeTypeMapping.put(MIME_IMAGE_TIFF, "tif");
mimeTypeMapping.put(MIME_IMAGE_PNG, "png");
byte[] decodedFile = Base64.getDecoder().decode(justification);
String formatName =
URLConnection.guessContentTypeFromStream(new ByteArrayInputStream(decodedFile));
String extension = "";
if (mimeTypeMapping.containsKey(formatName)) {
extension = "." + mimeTypeMapping.get(formatName);
File file = MetaFiles.createTempFile("justification", extension).toFile();
Files.write(decodedFile, file);
MetaFile metaFile = Beans.get(MetaFiles.class).upload(file);
expenseLine.setJustificationMetaFile(metaFile);
}
}
expense.addGeneralExpenseLineListItem(expenseLine);
expense = expenseService.compute(expense);
Beans.get(ExpenseRepository.class).save(expense);
HashMap<String, Object> data = new HashMap<>();
data.put("id", expenseLine.getId());
response.setData(data);
response.setTotal(1);
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/*
* This method is used in mobile application.
* It was in TimesheetServiceImpl
* @param request
* @param response
*
* POST /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:getActivities
* Content-Type: application/json
*
* URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:getActivities
* no field
*
* payload:
* { "data": {
* "action": "com.axelor.apps.hr.mobile.HumanResourceMobileController:getActivities"
* } }
*/
@Transactional
public void getActivities(ActionRequest request, ActionResponse response) {
List<Map<String, String>> dataList = new ArrayList<>();
try {
List<Product> productList =
Beans.get(ProductRepository.class).all().filter("self.isActivity = true").fetch();
for (Product product : productList) {
Map<String, String> map = new HashMap<>();
map.put("name", product.getName());
map.put("id", product.getId().toString());
dataList.add(map);
}
response.setData(dataList);
} catch (Exception e) {
response.setStatus(-1);
response.setError(e.getMessage());
}
}
/*
* This method is used in mobile application.
* It was in TimesheetServiceImpl
* @param request
* @param response
*
* POST /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:insertOrUpdateTSLine
* Content-Type: application/json
*
* URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:insertOrUpdateTSLine
* fields: (id,) project, activity, date, duration, comments
*
* payload:
* { "data": {
* "action": "com.axelor.apps.hr.mobile.HumanResourceMobileController:insertOrUpdateTSLine",
* "id": 1,
* "project": 1,
* "activity": 2,
* "date": "2018-02-22",
* "duration": 10,
* "comments": "no"
* } }
*/
@Transactional
public void insertOrUpdateTSLine(
ActionRequest request, ActionResponse response) { // insert TimesheetLine
try {
Map<String, Object> requestData = request.getData();
User user = AuthUtils.getUser();
Project project =
Beans.get(ProjectRepository.class)
.find(new Long(request.getData().get("project").toString()));
Product product =
Beans.get(ProductRepository.class)
.find(new Long(request.getData().get("activity").toString()));
LocalDate date =
LocalDate.parse(request.getData().get("date").toString(), DateTimeFormatter.ISO_DATE);
TimesheetRepository timesheetRepository = Beans.get(TimesheetRepository.class);
TimesheetService timesheetService = Beans.get(TimesheetService.class);
TimesheetLineService timesheetLineService = Beans.get(TimesheetLineService.class);
if (user != null) {
Timesheet timesheet =
timesheetRepository
.all()
.filter("self.statusSelect = 1 AND self.user.id = ?1", user.getId())
.order("-id")
.fetchOne();
if (timesheet == null) {
timesheet = timesheetService.createTimesheet(user, date, date);
}
BigDecimal hours = new BigDecimal(request.getData().get("duration").toString());
TimesheetLine line;
Object idO = requestData.get("id");
if (idO != null) {
line =
timesheetLineService.updateTimesheetLine(
Beans.get(TimesheetLineRepository.class).find(Long.valueOf(idO.toString())),
project,
product,
user,
date,
timesheet,
hours,
request.getData().get("comments").toString());
} else {
line =
timesheetLineService.createTimesheetLine(
project,
product,
user,
date,
timesheet,
hours,
request.getData().get("comments").toString());
}
// convert hours to what is defined in timeLoggingPreferenceSelect
BigDecimal duration = timesheetLineService.computeHoursDuration(timesheet, hours, false);
line.setDuration(duration);
timesheet.addTimesheetLineListItem(line);
timesheetRepository.save(timesheet);
response.setTotal(1);
HashMap<String, Object> data = new HashMap<>();
data.put("id", line.getId());
response.setData(data);
}
} catch (Exception e) {
TraceBackService.trace(e);
}
}
/*
* This method is used in mobile application.
* It was in LeaveServiceImpl
* @param request
* @param response
*
* POST /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:insertLeave
* Content-Type: application/json
*
* URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:insertLeave
* fields: leaveReason, fromDateT, startOn, toDateT, endOn, comment
*
* payload:
* { "data": {
* "action": "com.axelor.apps.hr.mobile.HumanResourceMobileController:insertLeave",
* "leaveReason": 10,
* "fromDateT": "2018-02-22T10:30:00",
* "startOn": 1,
* "toDateT": "2018-02-24T:19:30:00",
* "endOn": 1,
* "comment": "no"
* } }
*/
@Transactional(rollbackOn = {Exception.class})
public void insertLeave(ActionRequest request, ActionResponse response) throws AxelorException {
AppBaseService appBaseService = Beans.get(AppBaseService.class);
User user = AuthUtils.getUser();
Map<String, Object> requestData = request.getData();
LeaveReason leaveReason =
Beans.get(LeaveReasonRepository.class)
.find(Long.valueOf(requestData.get("leaveReason").toString()));
Employee employee = user.getEmployee();
if (employee == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
user.getName());
}
if (leaveReason != null) {
LeaveRequest leave = new LeaveRequest();
leave.setUser(user);
Company company = null;
if (employee.getMainEmploymentContract() != null) {
company = employee.getMainEmploymentContract().getPayCompany();
}
leave.setCompany(company);
LeaveLine leaveLine =
Beans.get(LeaveLineRepository.class)
.all()
.filter("self.employee = ?1 AND self.leaveReason = ?2", employee, leaveReason)
.fetchOne();
if (leaveLine == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_LINE),
employee.getName(),
leaveReason.getLeaveReason());
}
leave.setLeaveLine(leaveLine);
leave.setRequestDate(appBaseService.getTodayDate());
if (requestData.get("fromDateT") != null) {
leave.setFromDateT(
LocalDateTime.parse(
requestData.get("fromDateT").toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
leave.setStartOnSelect(new Integer(requestData.get("startOn").toString()));
if (requestData.get("toDateT") != null) {
leave.setToDateT(
LocalDateTime.parse(
requestData.get("toDateT").toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
leave.setEndOnSelect(new Integer(requestData.get("endOn").toString()));
leave.setDuration(Beans.get(LeaveService.class).computeDuration(leave));
leave.setStatusSelect(LeaveRequestRepository.STATUS_AWAITING_VALIDATION);
if (requestData.get("comments") != null) {
leave.setComments(requestData.get("comments").toString());
}
leave = Beans.get(LeaveRequestRepository.class).save(leave);
response.setTotal(1);
response.setValue("id", leave.getId());
}
}
/*
* This method is used in mobile application.
* It was in LeaveServiceImpl
* @param request
* @param response
*
* POST /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:getLeaveReason
* Content-Type: application/json
*
* URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:getLeaveReason
* fields: no field
*
* payload:
* { "data": {
* "action": "com.axelor.apps.hr.mobile.HumanResourceMobileController:getLeaveReason"
* } }
*/
@Transactional
public void getLeaveReason(ActionRequest request, ActionResponse response) {
try {
User user = AuthUtils.getUser();
List<Map<String, String>> dataList = new ArrayList<>();
if (user == null || user.getEmployee() == null) {
List<LeaveReason> leaveReasonList = Beans.get(LeaveReasonRepository.class).all().fetch();
for (LeaveReason leaveReason : leaveReasonList) {
if (leaveReason.getUnitSelect() == LeaveReasonRepository.UNIT_SELECT_DAYS) {
Map<String, String> map = new HashMap<>();
map.put("name", leaveReason.getLeaveReason());
map.put("id", leaveReason.getId().toString());
dataList.add(map);
}
}
} else if (user.getEmployee() != null) {
List<LeaveLine> leaveLineList =
Beans.get(LeaveLineRepository.class)
.all()
.filter("self.employee = ?1", user.getEmployee())
.order("name")
.fetch();
String tmpName = "";
for (LeaveLine leaveLine : leaveLineList) {
String name = leaveLine.getName();
if (tmpName != name) {
Map<String, String> map = new HashMap<>();
map.put("name", leaveLine.getName());
map.put("id", leaveLine.getLeaveReason().getId().toString());
map.put("quantity", leaveLine.getQuantity().toString());
dataList.add(map);
}
tmpName = name;
}
}
response.setData(dataList);
response.setTotal(dataList.size());
} catch (Exception e) {
response.setStatus(-1);
response.setError(e.getMessage());
}
}
/*
* This method is used in mobile application.
* It was in ExpenseServiceImpl
* @param request
* @param response
*
* POST /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:getExpensesTypes
* Content-Type: application/json
*
* URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:getExpensesTypes
* fields: no field
*
* payload:
* { "data": {
* "action": "com.axelor.apps.hr.mobile.HumanResourceMobileController:getExpensesTypes"
* } }
*/
@Transactional
public void getExpensesTypes(ActionRequest request, ActionResponse response) {
List<Map<String, String>> dataList = new ArrayList<>();
try {
List<Product> productList =
Beans.get(ProductRepository.class)
.all()
.filter(
"self.expense = true AND coalesce(self.unavailableToUsers, false) = false AND coalesce(self.personalExpense, false) = false")
.fetch();
for (Product product : productList) {
Map<String, String> map = new HashMap<>();
map.put("name", product.getName());
map.put("id", product.getId().toString());
dataList.add(map);
}
response.setData(dataList);
response.setTotal(dataList.size());
} catch (Exception e) {
response.setStatus(-1);
response.setError(e.getMessage());
}
}
/*
* This method is used in mobile application.
* @param request
* @param response
*
* POST /open-suite-webapp/ws/action/com.axelor.apps.hr.mobile.HumanResourceMobileController:getKilometricAllowParam
* Content-Type: application/json
*
* URL: com.axelor.apps.hr.mobile.HumanResourceMobileController:getKilometricAllowParam
* fields: no field
*
* payload:
* { "data": {
* "action": "com.axelor.apps.hr.mobile.HumanResourceMobileController:getKilometricAllowParam"
* } }
*/
@Transactional
public void getKilometricAllowParam(ActionRequest request, ActionResponse response) {
List<Map<String, String>> dataList = new ArrayList<>();
try {
User user = AuthUtils.getUser();
List<EmployeeVehicle> employeeVehicleList =
Beans.get(EmployeeVehicleRepository.class)
.all()
.filter("self.employee = ?1", user.getEmployee())
.fetch();
// Not sorted by default ?
employeeVehicleList.sort(
(employeeVehicle1, employeeVehicle2) ->
employeeVehicle1
.getKilometricAllowParam()
.getCode()
.compareTo(employeeVehicle2.getKilometricAllowParam().getCode()));
for (EmployeeVehicle employeeVehicle : employeeVehicleList) {
Map<String, String> map = new HashMap<>();
map.put("name", employeeVehicle.getKilometricAllowParam().getName());
map.put("id", employeeVehicle.getKilometricAllowParam().getId().toString());
dataList.add(map);
}
response.setData(dataList);
} catch (Exception e) {
response.setStatus(-1);
response.setError(e.getMessage());
}
}
}

View File

@ -0,0 +1,128 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.module;
import com.axelor.app.AxelorModule;
import com.axelor.apps.account.db.repo.PartnerAccountRepository;
import com.axelor.apps.account.service.batch.BatchCreditTransferExpensePayment;
import com.axelor.apps.account.service.config.AccountConfigService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderLineOriginServiceImpl;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderMergeServiceImpl;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderServiceImpl;
import com.axelor.apps.base.db.repo.UserBaseRepository;
import com.axelor.apps.base.service.batch.MailBatchService;
import com.axelor.apps.hr.db.repo.EmployeeHRRepository;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.ExpenseHRRepository;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.apps.hr.db.repo.HrBatchHRRepository;
import com.axelor.apps.hr.db.repo.HrBatchRepository;
import com.axelor.apps.hr.db.repo.PartnerHRRepository;
import com.axelor.apps.hr.db.repo.ProjectHRRepository;
import com.axelor.apps.hr.db.repo.ProjectPlanningTimeHRRepository;
import com.axelor.apps.hr.db.repo.TSTimerRepository;
import com.axelor.apps.hr.db.repo.TeamTaskHRRepository;
import com.axelor.apps.hr.db.repo.TimesheetHRRepository;
import com.axelor.apps.hr.db.repo.TimesheetLineHRRepository;
import com.axelor.apps.hr.db.repo.TimesheetLineRepository;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
import com.axelor.apps.hr.db.repo.TimesheetTimerHRRepository;
import com.axelor.apps.hr.db.repo.UserHRRepository;
import com.axelor.apps.hr.service.app.AppHumanResourceService;
import com.axelor.apps.hr.service.app.AppHumanResourceServiceImpl;
import com.axelor.apps.hr.service.bankorder.BankOrderLineOriginServiceHRImpl;
import com.axelor.apps.hr.service.bankorder.BankOrderMergeHRServiceImpl;
import com.axelor.apps.hr.service.bankorder.BankOrderServiceHRImpl;
import com.axelor.apps.hr.service.batch.BatchCreditTransferExpensePaymentHR;
import com.axelor.apps.hr.service.batch.MailBatchServiceHR;
import com.axelor.apps.hr.service.config.AccountConfigHRService;
import com.axelor.apps.hr.service.employee.EmployeeService;
import com.axelor.apps.hr.service.employee.EmployeeServiceImpl;
import com.axelor.apps.hr.service.expense.ExpenseService;
import com.axelor.apps.hr.service.expense.ExpenseServiceImpl;
import com.axelor.apps.hr.service.extra.hours.ExtraHoursService;
import com.axelor.apps.hr.service.extra.hours.ExtraHoursServiceImpl;
import com.axelor.apps.hr.service.leave.LeaveService;
import com.axelor.apps.hr.service.leave.LeaveServiceImpl;
import com.axelor.apps.hr.service.DailyReportService;
import com.axelor.apps.hr.service.DailyReportServiceImpl;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherAdvanceService;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherAdvanceServiceImpl;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherMgtLineService;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherMgtLineServiceImpl;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherMgtService;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherMgtServiceImpl;
import com.axelor.apps.hr.service.project.ProjectPlanningTimeService;
import com.axelor.apps.hr.service.project.ProjectPlanningTimeServiceImpl;
import com.axelor.apps.hr.service.timesheet.TimesheetLineService;
import com.axelor.apps.hr.service.timesheet.TimesheetLineServiceImpl;
import com.axelor.apps.hr.service.timesheet.TimesheetReportService;
import com.axelor.apps.hr.service.timesheet.TimesheetReportServiceImpl;
import com.axelor.apps.hr.service.timesheet.TimesheetService;
import com.axelor.apps.hr.service.timesheet.TimesheetServiceImpl;
import com.axelor.apps.hr.service.timesheet.timer.TimesheetTimerService;
import com.axelor.apps.hr.service.timesheet.timer.TimesheetTimerServiceImpl;
import com.axelor.apps.hr.service.user.UserHrService;
import com.axelor.apps.hr.service.user.UserHrServiceImpl;
import com.axelor.apps.project.db.repo.ProjectManagementRepository;
import com.axelor.apps.project.db.repo.ProjectPlanningTimeRepository;
import com.axelor.apps.project.db.repo.TeamTaskProjectRepository;
import com.axelor.apps.hr.service.AuthorizationService;
import com.axelor.apps.hr.service.AuthorizationServiceImpl;
public class HumanResourceModule extends AxelorModule {
@Override
protected void configure() {
bind(EmployeeService.class).to(EmployeeServiceImpl.class);
bind(TimesheetService.class).to(TimesheetServiceImpl.class);
bind(TimesheetLineService.class).to(TimesheetLineServiceImpl.class);
bind(TimesheetTimerService.class).to(TimesheetTimerServiceImpl.class);
bind(TimesheetRepository.class).to(TimesheetHRRepository.class);
bind(TimesheetLineRepository.class).to(TimesheetLineHRRepository.class);
bind(TSTimerRepository.class).to(TimesheetTimerHRRepository.class);
bind(MailBatchService.class).to(MailBatchServiceHR.class);
bind(AccountConfigService.class).to(AccountConfigHRService.class);
bind(ExtraHoursService.class).to(ExtraHoursServiceImpl.class);
bind(LeaveService.class).to(LeaveServiceImpl.class);
bind(ExpenseService.class).to(ExpenseServiceImpl.class);
bind(LunchVoucherMgtService.class).to(LunchVoucherMgtServiceImpl.class);
bind(LunchVoucherMgtLineService.class).to(LunchVoucherMgtLineServiceImpl.class);
bind(AppHumanResourceService.class).to(AppHumanResourceServiceImpl.class);
bind(LunchVoucherAdvanceService.class).to(LunchVoucherAdvanceServiceImpl.class);
bind(UserHrService.class).to(UserHrServiceImpl.class);
bind(ExpenseRepository.class).to(ExpenseHRRepository.class);
bind(EmployeeRepository.class).to(EmployeeHRRepository.class);
bind(BatchCreditTransferExpensePayment.class).to(BatchCreditTransferExpensePaymentHR.class);
bind(BankOrderServiceImpl.class).to(BankOrderServiceHRImpl.class);
bind(BankOrderLineOriginServiceImpl.class).to(BankOrderLineOriginServiceHRImpl.class);
bind(HrBatchRepository.class).to(HrBatchHRRepository.class);
bind(ProjectPlanningTimeRepository.class).to(ProjectPlanningTimeHRRepository.class);
bind(ProjectPlanningTimeService.class).to(ProjectPlanningTimeServiceImpl.class);
bind(ProjectManagementRepository.class).to(ProjectHRRepository.class);
bind(TeamTaskProjectRepository.class).to(TeamTaskHRRepository.class);
bind(UserBaseRepository.class).to(UserHRRepository.class);
bind(PartnerAccountRepository.class).to(PartnerHRRepository.class);
bind(BankOrderMergeServiceImpl.class).to(BankOrderMergeHRServiceImpl.class);
bind(TimesheetReportService.class).to(TimesheetReportServiceImpl.class);
bind(DailyReportService.class).to(DailyReportServiceImpl.class);
bind(AuthorizationService.class).to(AuthorizationServiceImpl.class);
bind(ExtraHoursService.class).to(ExtraHoursServiceImpl.class);
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.report;
public interface IReport {
public static final String EMPLYOMENT_CONTRACT = "EmploymentContract.rptdesign";
public static final String EXPENSE = "Expense.rptdesign";
public static final String TIMESHEET = "Timesheet.rptdesign";
public static final String EMPLOYEE_BONUS_MANAGEMENT = "EmployeeBonusMgt.rptdesign";
public static final String EMPLOYEE_ANNUAL_REPORT = "EmployeeAnnualReport.rptdesign";
public static final String LUNCH_VOUCHER_MGT_MONTHLY = "LunchVoucherMgt_Monthly.rptdesign";
public static final String LUNCH_VOUCHER_ADVANCE = "LunchVoucherAdvance.rptdesign";
public static final String EMPLOYEE_PHONEBOOK = "EmployeePhoneBook.rptdesign";
public static final String EMPLOYEE_TIMESHEET = "EmployeeTimesheet.rptdesign";
}

View File

@ -0,0 +1,96 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.report;
public class ITranslation {
public static final String EXPENSE_EXPENSE_OF = /*$$(*/ "Expense.expenseOf"; /*)*/
public static final String EXPENSE_EMPLOYEE_NAME = /*$$(*/ "Expense.employeeName"; /*)*/
public static final String EXPENSE_DATE = /*$$(*/ "Expense.expenseDate"; /*)*/
public static final String EXPENSE_PERIOD = /*$$(*/ "Expense.period"; /*)*/
public static final String EXPENSE_COMMENTS = /*$$(*/ "Expense.comments"; /*)*/
public static final String EXPENSE_AMOUNT = /*$$(*/ "Expense.totalAmount"; /*)*/
public static final String EXPENSE_EMPLOYEE_SIGNATURE = /*$$(*/ "Expense.employeeSignature"; /*)*/
public static final String EXPENSE_EMPLOYER_SIGNATURE = /*$$(*/ "Expense.employerSignature"; /*)*/
public static final String EXPENSE_WITHDRAWN_CASH = /*$$(*/ "Expense.withdrawnCash"; /*)*/
public static final String EXPENSE_PERSONAL_EXPENSE_AMOUNT = /*$$(*/
"Expense.personalExpenseAmount"; /*)*/
public static final String EXPENSE_COMPANY_CB_SELECT = /*$$(*/ "Expense.companyCbSelect"; /*)*/
public static final String EXPENSE_ADVANCE_AMOUNT = /*$$(*/ "Expense.advanceAmount"; /*)*/
public static final String EXPENSE_MULTIPLE_USERS = /*$$(*/ "Expense.multipleUsers"; /*)*/
public static final String EXPENSE_TOTAL_LABEL = /*$$(*/ "Expense.total"; /*)*/
public static final String EXPENSE_CURRENCY = /*$$(*/ "Expense.currency"; /*)*/
public static final String EXPENSE_GENERAL_EXPENSES = /*$$(*/ "General Expenses"; /*)*/
public static final String EXPENSE_LINE_EXPENSE_DATE = /*$$(*/ "ExpenseLine.expenseDate"; /*)*/
public static final String EXPENSE_LINE_CLIENT = /*$$(*/ "ExpenseLine.client"; /*)*/
public static final String EXPENSE_LINE_PROJECT = /*$$(*/ "ExpenseLine.project"; /*)*/
public static final String EXPENSE_LINE_EXPENSE_TYPE = /*$$(*/ "ExpenseLine.expenseType"; /*)*/
public static final String EXPENSE_LINE_AMOUNT = /*$$(*/ "ExpenseLine.amount"; /*)*/
public static final String EXPENSE_LINE_UNTAXED_AMOUNT = /*$$(*/
"ExpenseLine.untaxedAmount"; /*)*/
public static final String EXPENSE_LINE_TOTAL_TAXED = /*$$(*/ "ExpenseLine.totalTax"; /*)*/
public static final String EXPENSE_LINE_DISTANCE = /*$$(*/ "ExpenseLine.distance"; /*)*/
public static final String EXPENSE_LINE_CITY_FROM = /*$$(*/ "ExpenseLine.cityFrom"; /*)*/
public static final String EXPENSE_LINE_CITY_TO = /*$$(*/ "ExpenseLine.cityTo"; /*)*/
public static final String EXPENSE_LINE_KILOMETRIC_ALLOW_PARAM = /*$$(*/
"ExpenseLine.kilometricAllowParam"; /*)*/
public static final String EXPENSE_LINE_KILOMETRIC_EXPENSE_TYPE = /*$$(*/
"ExpenseLine.kilometricExpenseType"; /*)*/
public static final String TIMESHEET_OF = /*$$(*/ "Timesheet.timesheetOf"; /*)*/
public static final String TIMESHEET_PROJECT = /*$$(*/ "TimesheetLine.project"; /*)*/
public static final String TIMESHEET_PRODUCT = /*$$(*/ "TimesheetLine.product"; /*)*/
public static final String TIMESHEET_DURATION = /*$$(*/ "TimesheetLine.duration"; /*)*/
public static final String TIMESHEET_TOTAL_DURATION = /*$$(*/ "Timesheet.totalDuration"; /*)*/
public static final String ANNUAL_REPORT_OF = /*$$(*/ "Employee.annualReportOf"; /*)*/
public static final String TOTAL_TO_REFUND = /*$$(*/ "TotalToRefund"; /*)*/
public static final String LUNCH_VOUCHER_SIGNATURE_DOCUMENT = /*$$(*/
"LunchVoucher.signatureDocument"; /*)*/
public static final String LUNCH_VOUCHER_MONTH = /*$$(*/ "LunchVoucher.month"; /*)*/
public static final String LUNCH_VOUCHER_FROM = /*$$(*/ "LunchVoucher.lvFrom"; /*)*/
public static final String LUNCH_VOUCHER_LEAVES_FROM = /*$$(*/ "LunchVoucher.leavesFrom"; /*)*/
public static final String LUNCH_VOUCHER_TO = /*$$(*/ "LunchVoucher.to"; /*)*/
public static final String LUNCH_VOUCHER_NAMES = /*$$(*/ "LunchVoucher.names"; /*)*/
public static final String LUNCH_VOUCHER_QTY = /*$$(*/ "LunchVoucher.qty"; /*)*/
public static final String LUNCH_VOUCHER_AMOUNT = /*$$(*/ "LunchVoucher.amount"; /*)*/
public static final String LUNCH_VOUCHER_COMMENT = /*$$(*/ "LunchVoucher.comment"; /*)*/
public static final String LUNCH_VOUCHER_EMPLOYER_SHARE = /*$$(*/
"LunchVoucher.employerShare"; /*)*/
public static final String LUNCH_VOUCHER_EMPLOYEE_SHARE = /*$$(*/
"LunchVoucher.employeeShare"; /*)*/
public static final String LUNCH_VOUCHER_TOTAL = /*$$(*/ "LunchVoucher.total"; /*)*/
public static final String LUNCH_VOUCHER_SUBTOTAL = /*$$(*/ "LunchVoucher.subtotal"; /*)*/
public static final String LUNCH_VOUCHER_HANDED_ON = /*$$(*/ "LunchVoucher.handedOn"; /*)*/
public static final String LUNCH_VOUCHER_SIGNATURE = /*$$(*/ "LunchVoucher.signature"; /*)*/
public static final String EDITOR_ADD_LINE = /*$$(*/ "Add a line"; /*)*/
public static final String PHONE_BOOK_COMPANY_PHONE_BOOK = /*$$(*/
"Phonebook.employeePhonebook"; /*)*/
public static final String EMPLOYEE_TIMESHEET_FROM_DATE = /*$$(*/
"EmployeeTimesheet.fromDate"; /*)*/
public static final String EMPLOYEE_TIMESHEET_TO_DATE = /*$$(*/ "EmployeeTimesheet.toDate"; /*)*/
public static final String EMPLOYEE_TIMESHEET_USER = /*$$(*/ "EmployeeTimesheet.user"; /*)*/
public static final String EMPLOYEE_TIMESHEET_WEEK_TOTAL = /*$$(*/
"EmployeeTimesheet.weekTotal"; /*)*/
}

View File

@ -0,0 +1,28 @@
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.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public interface AbsenceService {
@Transactional
void attachTheAbsenceWithDailyReport(Absence absence, List<DailyReport> dailyreports);
@Transactional
void deAttachTheAbsenceWithDailyReport(
Absence absence, List<DailyReport> dailyreports, Boolean archive);
@Transactional
void chooseAbsenceType(Absence absence, Integer selectedType);
@Transactional
BigDecimal calculateTotalAbsenceHours(
LocalDateTime absenceStartDate, LocalDateTime absenceEndDate);
BigDecimal calculateTotalAbsenceMinutes(
LocalDateTime absenceStartDate, LocalDateTime absenceEndDate);
}

View File

@ -0,0 +1,149 @@
package com.axelor.apps.hr.service;
import com.axelor.apps.base.db.EventsPlanningLine;
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.axelor.inject.Beans;
import com.google.inject.Inject;
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.temporal.ChronoUnit;
import java.util.List;
public class AbsenceServiceImpl implements AbsenceService {
private final AbsenceRepository absenceRepository;
private final DailyReportRepository dailyReportRepository;
private List<EventsPlanningLine> 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<DailyReport> dailyreports) {
// Iterate over each DailyReport in the list
for (DailyReport dailyreport : dailyreports) {
dailyreport.addAbsenceSetItem(absence); // Set the absence for each report
dailyReportRepository.save(dailyreport);
}
}
@Transactional
public void deAttachTheAbsenceWithDailyReport(
Absence absence, List<DailyReport> dailyreports, Boolean archive) {
if (dailyreports != null) {
// Iterate over each DailyReport in the list
for (DailyReport dailyreport : dailyreports) {
dailyreport.removeAbsenceSetItem(null); // Set the absence for each report
dailyReportRepository.save(dailyreport);
}
}
if (archive) {
absence.setArchived(true);
absenceRepository.save(absence);
}
}
@Transactional
public void chooseAbsenceType(Absence absence, Integer selectedType) {
// Set the selected absence type to the absence entity
// Assuming you have a field named 'typeAbsence' in the Absence entity
absence.setAbsenceType(selectedType);
// 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;
}
public BigDecimal calculateTotalAbsenceMinutes(
LocalDateTime absenceStartDate, LocalDateTime absenceEndDate) {
// Calculate the duration between the two LocalDateTime objects
Duration duration = Duration.between(absenceStartDate, absenceEndDate);
// Convert the duration to minutes and then divide by 60 to get hours
long minutes = duration.toMinutes();
BigDecimal hours =
BigDecimal.valueOf(minutes).divide(BigDecimal.valueOf(60), 2, BigDecimal.ROUND_HALF_UP);
return customRound(hours);
}
private boolean isSpecialOvertimeDay(LocalDate date) {
for (EventsPlanningLine line : eventsPlanningLines) {
if (line.getDate().equals(date)) {
return true;
}
}
return false;
}
public static BigDecimal customRound(BigDecimal value) {
// Get the fractional part of the number
BigDecimal fractionalPart = value.remainder(BigDecimal.ONE);
// Define the intervals and their corresponding rounded values
if (fractionalPart.compareTo(new BigDecimal("0.125")) < 0) {
return value.setScale(0, RoundingMode.DOWN);
} else if (fractionalPart.compareTo(new BigDecimal("0.125")) >= 0
&& fractionalPart.compareTo(new BigDecimal("0.375")) < 0) {
return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.25"));
} else if (fractionalPart.compareTo(new BigDecimal("0.375")) >= 0
&& fractionalPart.compareTo(new BigDecimal("0.625")) < 0) {
return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.50"));
} else if (fractionalPart.compareTo(new BigDecimal("0.625")) >= 0
&& fractionalPart.compareTo(new BigDecimal("0.875")) < 0) {
return value.setScale(0, RoundingMode.DOWN).add(new BigDecimal("0.75"));
} else if (fractionalPart.compareTo(new BigDecimal("0.875")) >= 0) {
return value.setScale(0, RoundingMode.UP);
} else {
return value; // In case no rounding is needed
}
}
}

View File

@ -0,0 +1,20 @@
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;
@Transactional(rollbackOn = {Exception.class})
public void saveAP(JSONObject jsonObject) throws AxelorException;
}

View File

@ -0,0 +1,668 @@
package com.axelor.apps.hr.service;
import com.axelor.apps.hr.db.Authorization;
import com.axelor.apps.hr.db.DailyReport;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.repo.AuthorizationRepository;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
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 javax.inject.Inject;
import wslite.json.JSONException;
import wslite.json.JSONObject;
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.setIsAuthorizedLateArrival(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.setIsAuthorizedLateArrival(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.setIsAuthorizedEarlyDeparture(true);
}
} else if (previousStatus == 2 && newStatus == 4) {
System.out.println(
"Tickets :" + idInt + " Status changed from " + previousStatus + " to " + newStatus);
authorization.setRefusedByEmployee(validatedByEmployee);
authorization.setRefusalDate(validationDate);
authorization.setStatusSelect(newStatus);
// Save the updated Authorization
authorizationRepository.save(authorization);
}
} else {
String updatedDescription = commentaire + " - " + heure_sortie;
// Create an instance of Authorization
authorization = new Authorization();
authorization.setEmployee(employee);
authorization.setTicketId(idInt);
authorization.setTicket(ticketLink);
authorization.setRequisitionDate(absenceDate);
authorization.setDescription(updatedDescription);
authorization.setStatusSelect(validation_status);
authorization.setAuthorizationType(0);
if (validation_status == 3) {
authorization.setValidatedByEmployee(validatedByEmployee);
authorization.setValidationDate(validationDate);
} else if (validation_status == 4) {
authorization.setRefusedByEmployee(validatedByEmployee);
authorization.setRefusalDate(validationDate);
}
// Get Daily report
DailyReport dailyReport =
dailyReportRepo
.all()
.filter("self.employee = :employee and self.reportDate = :reportDate")
.bind("employee", employee)
.bind("reportDate", absenceDate)
.fetchOne();
if (dailyReport != null) {
authorization.setDailyReport(dailyReport);
if (validation_status == 3) dailyReport.setIsAuthorizedEarlyDeparture(true);
}
// Save the new Authorization
authorizationRepository.save(authorization);
}
} catch (JSONException e) {
System.err.println("Failed to parse JSON: " + jsonObject.toString());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional(rollbackOn = {Exception.class})
public void saveAP(JSONObject jsonObject) throws AxelorException {
try {
// Extract fields
int idInt = jsonObject.getInt("id");
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;
}
}
}

View File

@ -0,0 +1,22 @@
package com.axelor.apps.hr.service;
import com.axelor.apps.hr.db.DailyReport;
import com.google.inject.persist.Transactional;
import com.axelor.exception.AxelorException;
import java.math.BigDecimal;
import java.util.List;
public interface DailyReportService {
@Transactional
void workingHours(DailyReport DailyReport);
@Transactional
void determineShift(DailyReport DailyReport);
@Transactional(rollbackOn = {Exception.class})
public void deducePrimes(DailyReport DailyReport, Integer primeSelection, BigDecimal valueToDeduce) throws AxelorException ;
@Transactional(rollbackOn = {Exception.class})
public void massDeducePrimes(List<DailyReport> DailyReportList, Integer primeSelection, BigDecimal valueToDeduce) throws AxelorException;
}

View File

@ -0,0 +1,750 @@
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.EventsPlanningLineRepository;
import com.axelor.apps.hr.db.Absence;
import com.axelor.apps.hr.db.Authorization;
import com.axelor.apps.hr.db.DailyReport;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.OffDayWork;
import com.axelor.apps.hr.db.Shift;
import com.axelor.apps.hr.db.repo.AbsenceRepository;
import com.axelor.apps.hr.db.repo.AuthorizationRepository;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
import com.axelor.apps.hr.db.repo.OffDayWorkRepository;
import com.axelor.apps.hr.db.repo.ShiftRepository;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
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.util.ArrayList;
import java.util.List;
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<EventsPlanningLine> 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();
LocalDateTime firstEnter, lastQuit;
LocalTime shiftStartHour = null,
shiftEndHour = null,
shiftStartPauseHour = null,
shiftEndPauseHour = null;
if (shift != null) {
shiftStartHour = dailyReport.getShift().getStartHour();
shiftEndHour = dailyReport.getShift().getEndHour();
shiftStartPauseHour = dailyReport.getShift().getStartPause();
shiftEndPauseHour = dailyReport.getShift().getEndPause();
}
if (enters[0] != null && quits[0] != null) {
// Calculate total work duration
Duration totalDuration = Duration.ZERO;
Duration totalSupDuration = Duration.ZERO;
Duration nightDuration = Duration.ZERO;
Duration breakDuration = Duration.ZERO;
firstEnter = enters[0];
lastQuit = quits[0];
for (int i = 0; i < enters.length; i++) {
if (enters[i] != null && quits[i] != null) {
// 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(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(totalNightHours);
// Break Hours
breakDuration = calculateBreakDuration(enters, quits);
long breakMinutes = breakDuration.toMinutes();
BigDecimal breakHours =
BigDecimal.valueOf(breakMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
dailyReport.setBreakHours(breakHours);
if (shiftStartPauseHour != null && shiftEndPauseHour != null) {
boolean allInAllowedRange =
areBreaksInAllowedRange(enters, quits, shiftStartPauseHour, shiftEndPauseHour);
dailyReport.setBreakNotInTheAllowedRange(allInAllowedRange);
}
// shift 2
if (shift == 2) {
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(extraHours100);
dailyReport.setExtraHours50(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(totalHours.subtract(totalNightHours));
dailyReport.setExtraHours100(totalNightHours);
dailyReport.setAllowanceRecall(
totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
} else {
dailyReport.setExtraHours50(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(totalHours);
dailyReport.setAllowanceRecall(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
} else {
if (shift == 0 || shift == 3) {
dailyReport.setExtraHours50(totalSupHours.subtract(totalNightHours));
dailyReport.setExtraHours100(totalNightHours);
dailyReport.setAllowance(totalHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
dailyReport.setAllowanceRecall(
totalSupHours.compareTo(BigDecimal.valueOf(5)) >= 0 ? 1 : 0);
} else {
dailyReport.setExtraHours50(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(calculateItp(totalHours, shift, dailyReport.getHasItp()));
dailyReport.setIsCalculated(true);
} else if (enters[0] != null && quits[0] == null) {
// When the employee registers attendance only once
if (shift != 2) {
dailyReport.setWorkHours(BigDecimal.valueOf(8));
dailyReport.setAbsenceHours(BigDecimal.valueOf(0));
dailyReport.setIsCalculated(true);
}
} else if (enters.length != quits.length) {
Duration totalDuration = Duration.ZERO;
firstEnter = enters[0];
lastQuit = quits[quits.length - 1];
totalDuration = totalDuration.plus(Duration.between(firstEnter, lastQuit));
long totalMinutes = totalDuration.toMinutes();
BigDecimal totalHours =
BigDecimal.valueOf(totalMinutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
dailyReport.setWorkHours(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 {
dailyReport.setWorkHours(BigDecimal.valueOf(0));
dailyReport.setNightHours(BigDecimal.valueOf(0));
dailyReport.setBreakHours(BigDecimal.valueOf(0));
dailyReport.setExtraHours50(BigDecimal.valueOf(0));
dailyReport.setExtraHours100(BigDecimal.valueOf(0));
dailyReport.setItp(BigDecimal.valueOf(0));
dailyReport.setLateArrival(BigDecimal.valueOf(0));
dailyReport.setEarlyDeparture(BigDecimal.valueOf(0));
dailyReport.setAllowance(0);
dailyReport.setAllowanceRecall(0);
dailyReport.setBreakNotInTheAllowedRange(false);
dailyReport.setShift(Beans.get(ShiftRepository.class).find(50L));
System.err.println("Warning: no attendances for this day.");
dailyReport.setIsCalculated(true);
}
// Absences
if (dailyReport.getAbsence() == null) {
Absence absence =
Beans.get(AbsenceRepository.class)
.all()
.filter(
"self.employee = :employee and self.startDate <= :reportDate and self.endDate >= :reportDate and (self.archived = false or self.archived is null)")
.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) {
List<Authorization> authorizations =
Beans.get(AuthorizationRepository.class)
.all()
.filter("self.employee = :employee and self.requisitionDate = :reportDate")
.bind("employee", employee)
.bind("reportDate", reportDate)
.fetch();
if (authorizations != null) {
List<Authorization> authorizationList =
new ArrayList<>(); // Create a new list for authorizations
for (Authorization authorization : authorizations) {
authorization.setDailyReport(dailyReport);
authorizationList.add(authorization); // Add each authorization to the list
// Check authorization type and set corresponding flags in dailyReport
if (authorization.getAuthorizationType() == 0 && authorization.getStatusSelect() == 3) {
dailyReport.setIsAuthorizedEarlyDeparture(true);
}
if (authorization.getAuthorizationType() == 1 && authorization.getStatusSelect() == 3) {
dailyReport.setIsAuthorizedLateArrival(true);
}
if (authorization.getAuthorizationType() == 2 && authorization.getStatusSelect() == 3) {
dailyReport.setIsAuthorizedAbsence(true);
}
}
// Set the authorization list to dailyReport
dailyReport.setAuthorizationList(authorizationList);
}
}
// 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(BigDecimal.valueOf(8).subtract(dailyReport.getWorkHours()));
// Create Absence AI
if (dailyReport.getAbsenceSet().isEmpty()) {
Boolean isAuthorizedAbsence = dailyReport.getIsAuthorizedAbsence();
Boolean isAuthorizedLateArrival = dailyReport.getIsAuthorizedLateArrival();
Boolean isAuthorizedEarlyDeparture = dailyReport.getIsAuthorizedEarlyDeparture();
// AI all day
if (dailyReport.getAbsenceHours().compareTo(new BigDecimal("8")) == 0) {
if (!isAuthorizedAbsence) {
Absence absence = new Absence();
absence.setEmployee(employee);
absence.setAbsenceType(19); // Absence irrégulière
absence.setStartDate(reportDate.atStartOfDay());
absence.setEndDate(reportDate.atStartOfDay());
BigDecimal totalAbsenceHours =
Beans.get(AbsenceServiceImpl.class)
.calculateTotalAbsenceHours(
reportDate.atStartOfDay(), reportDate.atStartOfDay());
absence.setTotalAbsenceHours(totalAbsenceHours);
absenceRepository.save(absence);
dailyReport.addAbsenceSetItem(absence);
}
} else {
if (dailyReport.getShift().getMaxTimeLateArrival()
!= null) { // to check that is different to shift N/A
LocalTime firstEnterTime = dailyReport.getEnter1().toLocalTime();
if (firstEnterTime.isAfter(dailyReport.getShift().getMaxTimeLateArrival())
&& !isAuthorizedLateArrival) {
Absence absence = new Absence();
absence.setEmployee(employee);
absence.setAbsenceType(20); // Retard irrégulier
absence.setStartDate(reportDate.atTime(shiftStartHour));
absence.setEndDate(dailyReport.getEnter1());
BigDecimal totalAbsenceHours =
Beans.get(AbsenceServiceImpl.class)
.calculateTotalAbsenceMinutes(
reportDate.atTime(shiftStartHour), dailyReport.getEnter1());
absence.setTotalAbsenceHours(totalAbsenceHours);
absenceRepository.save(absence);
dailyReport.addAbsenceSetItem(absence);
}
if (dailyReport.getLastQuit() != null) {
LocalTime lastQuitTime = dailyReport.getLastQuit().toLocalTime();
if (lastQuitTime.isBefore(dailyReport.getShift().getMaxTimeEarlyDeparture())
&& !isAuthorizedEarlyDeparture) {
Absence absence = new Absence();
absence.setEmployee(employee);
absence.setAbsenceType(21); // Départ irrégulier
absence.setStartDate(dailyReport.getLastQuit());
absence.setEndDate(reportDate.atTime(shiftEndHour));
BigDecimal totalAbsenceHours =
Beans.get(AbsenceServiceImpl.class)
.calculateTotalAbsenceMinutes(
dailyReport.getLastQuit(), reportDate.atTime(shiftEndHour));
absence.setTotalAbsenceHours(totalAbsenceHours);
absenceRepository.save(absence);
dailyReport.addAbsenceSetItem(absence);
}
}
}
}
}
} else {
dailyReport.setAbsenceHours(BigDecimal.ZERO);
}
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<DailyReport> 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 calculateBreakDuration(LocalDateTime[] enters, LocalDateTime[] quits) {
Duration breakDuration = Duration.ZERO;
for (int i = 1; i < quits.length; i++) {
if (enters[i] != null && quits[i - 1] != null) {
breakDuration = breakDuration.plus(Duration.between(quits[i - 1], enters[i]));
}
}
return breakDuration;
}
private boolean areBreaksInAllowedRange(
LocalDateTime[] enters,
LocalDateTime[] quits,
LocalTime allowedStartTime,
LocalTime allowedEndTime) {
for (int i = 1; i < quits.length; i++) {
if (enters[i] != null && quits[i - 1] != null) {
LocalTime breakStartTime = quits[i - 1].toLocalTime();
LocalTime breakEndTime = enters[i].toLocalTime();
// Check if the break falls outside the allowed range
if (breakStartTime.isBefore(allowedStartTime) || breakEndTime.isAfter(allowedEndTime)) {
return false;
}
}
}
return true;
}
private BigDecimal calculateItp(BigDecimal totalHours, Integer shift, Boolean hasItp) {
// Shift 0 (no itp)
if (hasItp == true && shift != 0) return totalHours.min(BigDecimal.valueOf(8));
else return BigDecimal.ZERO;
}
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
}
}
@Transactional(rollbackOn = {Exception.class})
public void deducePrimes(
DailyReport dailyReport, Integer primeSelection, BigDecimal valueToDeduce)
throws AxelorException {
switch (primeSelection) {
case 1: // ITP
dailyReport.setDeduceItp(true);
dailyReport.setItpToDeduce(valueToDeduce);
break;
case 2: // Nuissance
dailyReport.setDeduceNuissance(true);
dailyReport.setItpToDeduce(valueToDeduce);
break;
case 3: // HS 50
dailyReport.setDeduceSupHours50(true);
dailyReport.setSupHours50ToDeduce(valueToDeduce);
break;
case 4: // HS 100
dailyReport.setDeduceSupHours100(true);
dailyReport.setSupHours100ToDeduce(valueToDeduce);
break;
default:
return; // Invalid configSelect, stop processing
}
Beans.get(DailyReportRepository.class).save(dailyReport);
}
@Transactional(rollbackOn = {Exception.class})
public void massDeducePrimes(
List<DailyReport> dailyReportList, Integer primeSelection, BigDecimal valueToDeduce)
throws AxelorException {
for (DailyReport dailyReport : dailyReportList) {
this.deducePrimes(dailyReport, primeSelection, valueToDeduce);
}
}
}

View File

@ -0,0 +1,110 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmployeeAdvance;
import com.axelor.apps.hr.db.EmployeeAdvanceUsage;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.repo.EmployeeAdvanceRepository;
import com.axelor.apps.hr.db.repo.EmployeeAdvanceUsageRepository;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.util.List;
public class EmployeeAdvanceService {
@Inject private EmployeeAdvanceRepository employeeAdvanceRepository;
@Inject private EmployeeAdvanceUsageRepository employeeAdvanceUsageRepository;
@Transactional
public void fillExpenseWithAdvances(Expense expense) {
Employee employee =
Beans.get(EmployeeRepository.class).find(expense.getUser().getEmployee().getId());
List<EmployeeAdvance> advanceList =
employeeAdvanceRepository
.all()
.filter(
"self.employee.id = ?1 AND self.remainingAmount > 0 AND self.date < ?2 AND self.statusSelect = ?3 AND self.typeSelect = ?4",
employee.getId(),
expense.getPeriod().getToDate(),
EmployeeAdvanceRepository.STATUS_VALIDATED,
EmployeeAdvanceRepository.TYPE_OCCASIONAL)
.fetch();
if (advanceList != null && !advanceList.isEmpty()) {
BigDecimal currentAmountToRefund =
expense
.getInTaxTotal()
.subtract(expense.getPersonalExpenseAmount())
.subtract(expense.getWithdrawnCash());
for (EmployeeAdvance advance : advanceList) {
if (currentAmountToRefund.signum() == 0) {
break;
}
currentAmountToRefund = withdrawFromAdvance(advance, expense, currentAmountToRefund);
employeeAdvanceRepository.save(advance);
}
expense.setAdvanceAmount(
expense
.getInTaxTotal()
.subtract(currentAmountToRefund)
.subtract(expense.getPersonalExpenseAmount())
.subtract(expense.getWithdrawnCash()));
}
}
public BigDecimal withdrawFromAdvance(
EmployeeAdvance employeeAdvance, Expense expense, BigDecimal maxAmount) {
if (maxAmount.compareTo(employeeAdvance.getRemainingAmount()) > 0) {
maxAmount = maxAmount.subtract(employeeAdvance.getRemainingAmount());
createEmployeeAdvanceUsage(employeeAdvance, expense, employeeAdvance.getRemainingAmount());
employeeAdvance.setRemainingAmount(BigDecimal.ZERO);
} else {
createEmployeeAdvanceUsage(employeeAdvance, expense, maxAmount);
employeeAdvance.setRemainingAmount(employeeAdvance.getRemainingAmount().subtract(maxAmount));
maxAmount = BigDecimal.ZERO;
}
return maxAmount;
}
@Transactional
public void createEmployeeAdvanceUsage(
EmployeeAdvance employeeAdvance, Expense expense, BigDecimal amount) {
EmployeeAdvanceUsage usage = new EmployeeAdvanceUsage();
usage.setEmployeeAdvance(employeeAdvance);
usage.setExpense(expense);
usage.setUsedAmount(amount);
employeeAdvanceUsageRepository.save(usage);
}
}

View File

@ -0,0 +1,190 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service;
import com.axelor.apps.base.db.Period;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmployeeBonusMgt;
import com.axelor.apps.hr.db.EmployeeBonusMgtLine;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.repo.EmployeeBonusMgtLineRepository;
import com.axelor.apps.hr.db.repo.EmployeeBonusMgtRepository;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.employee.EmployeeServiceImpl;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.tool.template.TemplateMaker;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
public class EmployeeBonusService {
@Inject EmployeeBonusMgtRepository employeeBonusMgtRepo;
@Inject EmployeeBonusMgtLineRepository employeeBonusMgtLineRepo;
@Inject EmployeeServiceImpl employeeService;
private static final char TEMPLATE_DELIMITER = '$';
@Transactional(rollbackOn = {Exception.class})
public void compute(EmployeeBonusMgt bonus) throws AxelorException {
Map<Employee, EmployeeBonusMgtLine> employeeStatus = new HashMap<>();
for (EmployeeBonusMgtLine line : bonus.getEmployeeBonusMgtLineList()) {
employeeStatus.put(line.getEmployee(), line);
}
List<Employee> allEmployee =
Beans.get(EmployeeRepository.class)
.all()
.filter("self.mainEmploymentContract.payCompany = ?1", bonus.getCompany())
.fetch();
TemplateMaker maker = new TemplateMaker(Locale.FRENCH, TEMPLATE_DELIMITER, TEMPLATE_DELIMITER);
String eval;
CompilerConfiguration conf = new CompilerConfiguration();
ImportCustomizer customizer = new ImportCustomizer();
customizer.addStaticStars("java.lang.Math");
conf.addCompilationCustomizers(customizer);
Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding, conf);
Integer employeeBonusStatus = EmployeeBonusMgtRepository.STATUS_CALCULATED;
for (Employee employee : allEmployee) {
// check if line is already calculated
if (employeeStatus.get(employee) != null) {
if (employeeStatus
.get(employee)
.getStatusSelect()
.equals(EmployeeBonusMgtLineRepository.STATUS_CALCULATED)) {
continue;
} else {
bonus.removeEmployeeBonusMgtLineListItem(employeeStatus.get(employee));
}
}
maker.setContext(employee, "Employee");
EmployeeBonusMgtLine line = new EmployeeBonusMgtLine();
line.setEmployeeBonusMgt(bonus);
line.setEmployee(employee);
maker.addInContext("EmployeeBonusMgtLine", line);
String formula = bonus.getEmployeeBonusType().getApplicationCondition();
Integer lineStatus = EmployeeBonusMgtLineRepository.STATUS_CALCULATED;
try {
formula =
replaceExpressionInFormula(
formula, bonus.getCompany().getHrConfig(), employee, bonus.getPayPeriod());
} catch (Exception e) {
TraceBackService.trace(e);
formula = "true";
lineStatus = EmployeeBonusMgtLineRepository.STATUS_ANOMALY;
}
maker.setTemplate(formula);
eval = maker.make();
if (shell.evaluate(eval).toString().equals("true")) {
try {
formula =
replaceExpressionInFormula(
bonus.getEmployeeBonusType().getFormula(),
bonus.getCompany().getHrConfig(),
employee,
bonus.getPayPeriod());
} catch (Exception e) {
lineStatus = EmployeeBonusMgtLineRepository.STATUS_ANOMALY;
}
line.setStatusSelect(lineStatus);
if (lineStatus.equals(EmployeeBonusMgtLineRepository.STATUS_ANOMALY)) {
employeeBonusStatus = EmployeeBonusMgtRepository.STATUS_ANOMALY;
employeeBonusMgtLineRepo.save(line);
continue;
}
line.setSeniorityDate(employee.getSeniorityDate());
line.setCoef(employee.getBonusCoef());
line.setWeeklyPlanning(employee.getWeeklyPlanning());
maker.setTemplate(formula);
eval = maker.make();
line.setAmount(new BigDecimal(shell.evaluate(eval).toString()));
employeeBonusMgtLineRepo.save(line);
}
}
bonus.setStatusSelect(employeeBonusStatus);
employeeBonusMgtRepo.save(bonus);
}
public String replaceExpressionInFormula(
String formula, HRConfig hrConfig, Employee employee, Period period) throws AxelorException {
if (!Strings.isNullOrEmpty(hrConfig.getAgeVariableName())) {
formula =
formula.replace(
hrConfig.getAgeVariableName(),
String.valueOf(employeeService.getAge(employee, period.getFromDate())));
}
if (!Strings.isNullOrEmpty(hrConfig.getSeniorityVariableName())) {
formula =
formula.replace(
hrConfig.getSeniorityVariableName(),
String.valueOf(employeeService.getLengthOfService(employee, period.getFromDate())));
}
if (!Strings.isNullOrEmpty(hrConfig.getWorkingDaysVariableName())) {
formula =
formula.replace(
hrConfig.getWorkingDaysVariableName(),
String.valueOf(
employeeService.getDaysWorkedInPeriod(
employee, period.getFromDate(), period.getToDate())));
}
if (!Strings.isNullOrEmpty(hrConfig.getTotalWorkingDaysVariableName())) {
formula =
formula.replace(
hrConfig.getTotalWorkingDaysVariableName(),
String.valueOf(
employeeService.getDaysWorksInPeriod(
employee, period.getFromDate(), period.getToDate())));
}
// For checking that formula contains variables like $*$
if (formula.matches("(\\$\\w+\\$).+")) {
throw new AxelorException(
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.HR_CONFIG_FORMULA_VARIABLE_MISSING),
hrConfig.getCompany().getName());
}
return formula;
}
}

View File

@ -0,0 +1,179 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service;
import com.axelor.app.AppSettings;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.db.Address;
import com.axelor.apps.base.db.Department;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.repo.EmploymentContractRepository;
import com.axelor.apps.hr.report.IReport;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class EmploymentContractService {
@Inject private EmploymentContractRepository employmentContractRepo;
@Transactional(rollbackOn = {Exception.class})
public int addAmendment(EmploymentContract employmentContract) throws AxelorException {
String name =
employmentContract.getFullName() + "_" + employmentContract.getEmploymentContractVersion();
ReportFactory.createReport(IReport.EMPLYOMENT_CONTRACT, name + "-${date}")
.addParam("ContractId", employmentContract.getId())
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.toAttach(employmentContract)
.generate()
.getFileLink();
int version = employmentContract.getEmploymentContractVersion() + 1;
employmentContract.setEmploymentContractVersion(version);
employmentContractRepo.save(employmentContract);
return version;
}
public void exportEmploymentContract(EmploymentContract employmentContract) throws IOException {
List<String[]> list = new ArrayList<>();
this.employmentContractExportSilae(employmentContract, list);
String fileName = this.employmentContractExportName() + ".csv";
String filePath = AppSettings.get().get("file.upload.dir");
new File(filePath).mkdirs();
String[] headers = employmentContractExportHeaders();
CsvTool.csvWriter(filePath, fileName, ';', headers, list);
Path path = Paths.get(filePath + System.getProperty("file.separator") + fileName);
try (InputStream is = new FileInputStream(path.toFile())) {
Beans.get(MetaFiles.class).attach(is, fileName, employmentContract);
}
}
public void employmentContractExportSilae(
EmploymentContract employmentContract, List<String[]> list) {
String[] item = new String[21];
item[0] = employmentContract.getId().toString();
item[1] =
employmentContract.getStartDate() == null
? ""
: employmentContract.getStartDate().toString();
item[2] =
employmentContract.getEndDate() == null ? "" : employmentContract.getEndDate().toString();
Employee employee = employmentContract.getEmployee();
item[4] = employee.getMaritalName();
Partner contactPartner = employee.getContactPartner();
if (contactPartner != null) {
item[3] = contactPartner.getName();
item[5] = contactPartner.getFirstName();
Address mainAddress = contactPartner.getMainAddress();
if (mainAddress != null) {
item[6] = mainAddress.getAddressL4();
item[7] = mainAddress.getAddressL2();
item[8] = mainAddress.getZip();
item[9] = mainAddress.getCity().getName();
}
item[10] = contactPartner.getMobilePhone();
item[11] = contactPartner.getFixedPhone();
item[12] =
contactPartner.getEmailAddress() == null
? ""
: contactPartner.getEmailAddress().getAddress();
}
item[13] = employee.getBirthDate() == null ? "" : employee.getBirthDate().toString();
Department birthDepartment = employee.getDepartmentOfBirth();
if (birthDepartment != null) {
item[14] = birthDepartment.getName();
item[16] = !birthDepartment.getCode().equals("99") ? "FR - FRANCE" : "Oui";
}
item[15] = employee.getCityOfBirth() == null ? "" : employee.getCityOfBirth().getName();
item[17] = employee.getSocialSecurityNumber();
item[18] = employmentContract.getPayCompany().getName();
item[19] =
employmentContract.getContractType() == null
? ""
: employmentContract.getContractType().getId().toString();
item[20] = employee.getMaidenName();
list.add(item);
}
public String employmentContractExportName() {
return I18n.get("Employment contract")
+ " - "
+ Beans.get(AppBaseService.class).getTodayDateTime().toLocalDateTime().toString();
}
public String[] employmentContractExportHeaders() {
String[] headers = {
I18n.get("MATRICULE"),
I18n.get("DATE D'ENTREE"),
I18n.get("DATE DE SORTIE"),
I18n.get("NOM"),
I18n.get("NOM MARITAL"),
I18n.get("PRENOM"),
I18n.get("VOIE"),
I18n.get("COMPLEMENT"),
I18n.get("CP"),
I18n.get("VILLE"),
I18n.get("TEL"),
I18n.get("TEL 2"),
I18n.get("E-MAIL"),
I18n.get("DATE NAISS"),
I18n.get("DEPT NAISS"),
I18n.get("LIEU"),
I18n.get("ETRANGER"),
I18n.get("NUMERO INSEE"),
I18n.get("ETAB"),
I18n.get("CDD"),
I18n.get("NOM DE JEUNE FILLE")
};
return headers;
}
}

View File

@ -0,0 +1,57 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service;
import com.axelor.apps.hr.db.Employee;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.db.JPA;
import com.axelor.db.Model;
public class HRMenuTagService {
/**
* @param modelConcerned
* @param status 1 : Draft 2 : Confirmed 3 : Validated 4 : Refused 5 : Canceled
* @return The number of records
*/
public <T extends Model> String countRecordsTag(Class<T> modelConcerned, int status) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
if (employee != null && employee.getHrManager()) {
return Long.toString(
JPA.all(modelConcerned)
.filter("self.statusSelect = :_statusSelect")
.bind("_statusSelect", status)
.count());
} else {
return Long.toString(
JPA.all(modelConcerned)
.filter(
"self.user.employee.managerUser.id = :_userId AND self.statusSelect = :_statusSelect")
.bind("_userId", user.getId())
.bind("_statusSelect", status)
.count());
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.auth.db.User;
import com.axelor.meta.schema.actions.ActionView;
public class HRMenuValidateService {
public void createValidateDomain(
User user, Employee employee, ActionView.ActionViewBuilder actionView) {
actionView
.domain("self.statusSelect = :_statusSelect")
.context("_statusSelect", ExpenseRepository.STATUS_CONFIRMED);
if (employee == null || !employee.getHrManager()) {
if (employee == null || employee.getManagerUser() == null) {
actionView
.domain(
actionView.get().getDomain()
+ " AND (self.user = :_user OR self.user.employee.managerUser = :_user)")
.context("_user", user);
} else {
actionView
.domain(actionView.get().getDomain() + " AND self.user.employee.managerUser = :_user")
.context("_user", user);
}
}
}
}

View File

@ -0,0 +1,484 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service;
import com.axelor.apps.base.db.AppBase;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Year;
import com.axelor.apps.base.db.repo.AppBaseRepository;
import com.axelor.apps.base.service.MapService;
import com.axelor.apps.base.service.YearServiceImpl;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.ExpenseLine;
import com.axelor.apps.hr.db.KilometricAllowanceRate;
import com.axelor.apps.hr.db.KilometricAllowanceRule;
import com.axelor.apps.hr.db.KilometricLog;
import com.axelor.apps.hr.db.repo.KilometricAllowanceRateRepository;
import com.axelor.apps.hr.db.repo.KilometricLogRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.hr.translation.ITranslation;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.common.ObjectUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.utils.URIBuilder;
import wslite.json.JSONException;
import wslite.json.JSONObject;
public class KilometricService {
private AppBaseService appBaseService;
private KilometricLogRepository kilometricLogRepo;
private MapService mapService;
@Inject
public KilometricService(
AppBaseService appBaseService,
KilometricLogRepository kilometricLogRepo,
MapService mapService) {
this.appBaseService = appBaseService;
this.kilometricLogRepo = kilometricLogRepo;
this.mapService = mapService;
}
public KilometricLog getKilometricLog(Employee employee, LocalDate refDate) {
for (KilometricLog log : employee.getKilometricLogList()) {
if (log.getYear().getFromDate().isBefore(refDate)
&& log.getYear().getToDate().isAfter(refDate)) {
return log;
}
}
return null;
}
public KilometricLog getCurrentKilometricLog(Employee employee) {
return getKilometricLog(employee, appBaseService.getTodayDate());
}
public KilometricLog createKilometricLog(Employee employee, BigDecimal distance, Year year) {
KilometricLog log = new KilometricLog();
log.setEmployee(employee);
log.setDistanceTravelled(distance);
log.setYear(year);
return log;
}
public KilometricLog getOrCreateKilometricLog(Employee employee, LocalDate date)
throws AxelorException {
KilometricLog log = getKilometricLog(employee, date);
if (log != null) {
return log;
}
if (employee.getMainEmploymentContract() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EMPLOYEE_CONTRACT_OF_EMPLOYMENT),
employee.getName());
}
Year year =
Beans.get(YearServiceImpl.class)
.getYear(date, employee.getMainEmploymentContract().getPayCompany());
if (year == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.KILOMETRIC_LOG_NO_YEAR),
employee.getMainEmploymentContract().getPayCompany(),
date);
}
return createKilometricLog(employee, new BigDecimal("0.00"), year);
}
public BigDecimal computeKilometricExpense(ExpenseLine expenseLine, Employee employee)
throws AxelorException {
BigDecimal distance = expenseLine.getDistance();
EmploymentContract mainEmploymentContract = employee.getMainEmploymentContract();
if (mainEmploymentContract == null || mainEmploymentContract.getPayCompany() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EMPLOYEE_CONTRACT_OF_EMPLOYMENT),
employee.getName());
}
Company company = mainEmploymentContract.getPayCompany();
KilometricLog log = getKilometricLog(employee, expenseLine.getExpenseDate());
BigDecimal previousDistance = log == null ? BigDecimal.ZERO : log.getDistanceTravelled();
KilometricAllowanceRate allowance =
expenseLine.getKilometricAllowParam() != null
? Beans.get(KilometricAllowanceRateRepository.class)
.all()
.filter(
"self.kilometricAllowParam.id = :_kilometricAllowParamId "
+ "and self.hrConfig.id = :_hrConfigId")
.bind("_kilometricAllowParamId", expenseLine.getKilometricAllowParam().getId())
.bind("_hrConfigId", Beans.get(HRConfigService.class).getHRConfig(company).getId())
.fetchOne()
: null;
if (allowance == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.KILOMETRIC_ALLOWANCE_RATE_MISSING),
expenseLine.getKilometricAllowParam() != null
? expenseLine.getKilometricAllowParam().getName()
: "",
company.getName());
}
List<KilometricAllowanceRule> ruleList = new ArrayList<>();
List<KilometricAllowanceRule> allowanceRuleList = allowance.getKilometricAllowanceRuleList();
if (ObjectUtils.notEmpty(allowanceRuleList)) {
for (KilometricAllowanceRule rule : allowanceRuleList) {
if (rule.getMinimumCondition().compareTo(previousDistance.add(distance)) <= 0
&& rule.getMaximumCondition().compareTo(previousDistance) >= 0) {
ruleList.add(rule);
}
}
}
if (ruleList.isEmpty()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.KILOMETRIC_ALLOWANCE_NO_RULE),
allowance.getKilometricAllowParam().getName());
}
BigDecimal price = BigDecimal.ZERO;
if (ruleList.size() == 1) {
price = distance.multiply(ruleList.get(0).getRate());
} else {
Collections.sort(
ruleList,
(object1, object2) ->
object1.getMinimumCondition().compareTo(object2.getMinimumCondition()));
for (KilometricAllowanceRule rule : ruleList) {
BigDecimal min = rule.getMinimumCondition().max(previousDistance);
BigDecimal max = rule.getMaximumCondition().min(previousDistance.add(distance));
price = price.add(max.subtract(min).multiply(rule.getRate()));
}
}
return price.setScale(2, RoundingMode.HALF_UP);
}
@Transactional(rollbackOn = {Exception.class})
public void updateKilometricLog(ExpenseLine expenseLine, Employee employee)
throws AxelorException {
KilometricLog log = getOrCreateKilometricLog(employee, expenseLine.getExpenseDate());
log.setDistanceTravelled(log.getDistanceTravelled().add(expenseLine.getDistance()));
if (log.getExpenseLineList() == null || !log.getExpenseLineList().contains(expenseLine)) {
log.addExpenseLineListItem(expenseLine);
}
kilometricLogRepo.save(log);
}
public BigDecimal computeDistance(ExpenseLine expenseLine) throws AxelorException {
return computeDistance(expenseLine.getFromCity(), expenseLine.getToCity());
}
/**
* Compute the distance between two cities.
*
* @param fromCity
* @param toCity
* @return
* @throws AxelorException
*/
protected BigDecimal computeDistance(String fromCity, String toCity) throws AxelorException {
BigDecimal distance = BigDecimal.ZERO;
if (StringUtils.isEmpty(fromCity)
|| StringUtils.isEmpty(toCity)
|| fromCity.equalsIgnoreCase(toCity)) return distance;
AppBase appBase = appBaseService.getAppBase();
try {
switch (appBase.getMapApiSelect()) {
case AppBaseRepository.MAP_API_GOOGLE:
distance = this.getDistanceUsingGoogle(fromCity, toCity);
break;
case AppBaseRepository.MAP_API_OPEN_STREET_MAP:
distance = this.getDistanceUsingOSM(fromCity, toCity);
break;
}
return distance;
} catch (URISyntaxException | IOException | JSONException e) {
throw new AxelorException(e, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR);
}
}
protected BigDecimal getDistanceUsingGoogle(String fromCity, String toCity)
throws JSONException, AxelorException, URISyntaxException, IOException {
User user = AuthUtils.getUser();
JSONObject json = getGoogleMapsDistanceMatrixResponse(fromCity, toCity, user.getLanguage());
String status = json.getString("status");
if (status.equals("OK")) {
JSONObject response =
json.getJSONArray("rows").getJSONObject(0).getJSONArray("elements").getJSONObject(0);
status = response.getString("status");
if (status.equals("OK")) {
return BigDecimal.valueOf(response.getJSONObject("distance").getDouble("value") / 1000);
}
}
String msg =
json.has("error_message")
? String.format("%s / %s", status, json.getString("error_message"))
: status;
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
IExceptionMessage.KILOMETRIC_ALLOWANCE_GOOGLE_MAPS_ERROR,
msg);
}
protected BigDecimal getDistanceUsingOSM(String fromCity, String toCity)
throws JSONException, AxelorException, URISyntaxException, IOException {
AppBase appBase = appBaseService.getAppBase();
BigDecimal distance = BigDecimal.ZERO;
if (appBase.getOsmRoutingServiceApiSelect() == AppBaseRepository.ROUTING_API_YOURS) {
distance = this.getDistanceUsingYOURSApi(fromCity, toCity);
} else if (appBase.getOsmRoutingServiceApiSelect() == AppBaseRepository.ROUTING_API_OSRM) {
distance = this.getDistanceUsingOSRMApi(fromCity, toCity);
}
return distance;
}
protected BigDecimal getDistanceUsingYOURSApi(String fromCity, String toCity)
throws AxelorException, JSONException, URISyntaxException, IOException {
BigDecimal distance = BigDecimal.ZERO;
JSONObject json = getYOURSApiResponse(fromCity, toCity);
distance = BigDecimal.valueOf(json.getJSONObject("properties").getDouble("distance"));
if (distance.compareTo(BigDecimal.ZERO) == 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
IExceptionMessage.KILOMETRIC_ALLOWANCE_OSM_ERROR,
ITranslation.NO_ROUTE);
}
return distance;
}
protected BigDecimal getDistanceUsingOSRMApi(String fromCity, String toCity)
throws AxelorException, JSONException, URISyntaxException, IOException {
JSONObject json = getOSRMApiResponse(fromCity, toCity);
String status = json.getString("code");
if (status.equals("Ok")) {
return BigDecimal.valueOf(
json.getJSONArray("routes").getJSONObject(0).getDouble("distance") / 1000);
}
String msg = json.has("message") ? String.format("%s", json.getString("message")) : status;
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
IExceptionMessage.KILOMETRIC_ALLOWANCE_OSM_ERROR,
msg);
}
/**
* Get JSON response from Google Maps Distance Matrix API.
*
* @param origins
* @param destinations
* @param language
* @return
* @throws URISyntaxException
* @throws IOException
* @throws JSONException
* @throws AxelorException
*/
protected JSONObject getGoogleMapsDistanceMatrixResponse(
String origins, String destinations, String language)
throws URISyntaxException, IOException, JSONException, AxelorException {
URIBuilder ub = new URIBuilder("https://maps.googleapis.com/maps/api/distancematrix/json");
ub.addParameter("origins", origins);
ub.addParameter("destinations", destinations);
ub.addParameter("language", language);
ub.addParameter("key", mapService.getGoogleMapsApiKey());
return this.getApiResponse(
ub.toString(), IExceptionMessage.KILOMETRIC_ALLOWANCE_GOOGLE_MAPS_ERROR);
}
/**
* Get JSON response from Open Street Route Machine API.
*
* @param origins
* @param destinations
* @return
* @throws AxelorException
* @throws JSONException
* @throws URISyntaxException
* @throws IOException
*/
protected JSONObject getOSRMApiResponse(String origins, String destinations)
throws AxelorException, JSONException, URISyntaxException, IOException {
Map<String, Object> originMap = this.getLocationMap(origins);
Map<String, Object> destinationMap = this.getLocationMap(destinations);
String originCoordinates = originMap.get("longitude") + "," + originMap.get("latitude");
String destinationCoordinates =
destinationMap.get("longitude") + "," + destinationMap.get("latitude");
String uri =
String.format(
"https://router.project-osrm.org/route/v1/driving/%s;%s",
originCoordinates, destinationCoordinates);
return this.getApiResponse(uri, IExceptionMessage.KILOMETRIC_ALLOWANCE_OSM_ERROR);
}
/**
* Get JSON response from YOURS(Yet Another Openstreetmap Route Service) API.
*
* @param origins
* @param destinations
* @return
* @throws AxelorException
* @throws JSONException
* @throws URISyntaxException
* @throws IOException
*/
protected JSONObject getYOURSApiResponse(String origins, String destinations)
throws AxelorException, JSONException, URISyntaxException, IOException {
Map<String, Object> originMap = this.getLocationMap(origins);
Map<String, Object> destinationMap = this.getLocationMap(destinations);
String flat = originMap.get("latitude").toString();
String flon = originMap.get("longitude").toString();
String tlat = destinationMap.get("latitude").toString();
String tlon = destinationMap.get("longitude").toString();
URIBuilder ub = new URIBuilder("http://www.yournavigation.org/api/1.0/gosmore.php");
ub.addParameter("format", "geojson");
ub.addParameter("flat", flat);
ub.addParameter("flon", flon);
ub.addParameter("tlat", tlat);
ub.addParameter("tlon", tlon);
ub.addParameter("v", "motorcar");
ub.addParameter("fast", "0");
return this.getApiResponse(ub.toString(), IExceptionMessage.KILOMETRIC_ALLOWANCE_OSM_ERROR);
}
protected Map<String, Object> getLocationMap(String location) throws AxelorException {
Map<String, Object> locationMap;
try {
locationMap = mapService.getMap(location);
} catch (Exception e) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
IExceptionMessage.KILOMETRIC_ALLOWANCE_OSM_ERROR,
e.getMessage());
}
if (locationMap == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
IExceptionMessage.KILOMETRIC_ALLOWANCE_OSM_ERROR,
ITranslation.NO_SUCH_PLACE);
}
return locationMap;
}
protected JSONObject getApiResponse(String urlString, String exceptionMessage)
throws IOException, JSONException, AxelorException {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();
this.checkResponseStatus(responseCode, exceptionMessage);
StringBuilder sb = new StringBuilder();
try (BufferedReader in =
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
sb.append(inputLine + "\n");
}
}
String response = sb.toString();
JSONObject json;
// throw exception if response is not json
try {
json = new JSONObject(response);
} catch (Exception e) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR, exceptionMessage, response);
}
return json;
}
protected void checkResponseStatus(int responseCode, String exceptionMessage)
throws AxelorException {
if (responseCode == 200) {
return;
} else if (responseCode == 429) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
exceptionMessage,
ITranslation.REQUEST_OVERFLOW);
} else {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
exceptionMessage,
"Server returned status code " + responseCode);
}
}
}

View File

@ -0,0 +1,20 @@
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.axelor.apps.hr.db.MonthlyReport;
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<DailyReport> employeeDailyReports, List<Absence> employeeAbsences);
void updateMensuelReport(MonthlyReport monthlyReport, Employee employee, Period period, LocalDate startDate, LocalDate endDate, List<DailyReport> employeeDailyReports, List<Absence> employeeAbsences);
/*void calculePrimeAssiduite(Mensuels mensuels);*/
}

View File

@ -0,0 +1,683 @@
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.DailyReport;
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.repo.AbsenceRepository;
import com.axelor.apps.hr.db.repo.AuthorizationRepository;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
import com.axelor.apps.hr.db.repo.MonthlyReportRepository;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
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<DailyReport> employeeDailyReports,
List<Absence> employeeAbsences) {
Boolean hasNuissance = employee.getHasNuissance();
LocalDate firstDayOfMonth = endDate.with(TemporalAdjusters.firstDayOfMonth());
// Perform calculations
Integer monthlyAllowance = 0;
Integer monthlyAllowanceRecall = 0;
BigDecimal monthlyNightHours = BigDecimal.ZERO;
BigDecimal monthlyExtraHours50 = BigDecimal.ZERO;
BigDecimal monthlyExtraHours100 = BigDecimal.ZERO;
BigDecimal excessHours50 = BigDecimal.ZERO;
BigDecimal excessHours100 = BigDecimal.ZERO;
BigDecimal totalSupHours = BigDecimal.ZERO;
BigDecimal monthlyITP = BigDecimal.ZERO;
BigDecimal totalNuissance = BigDecimal.ZERO;
BigDecimal totalAbsence = BigDecimal.ZERO;
BigDecimal totalWorkHours = BigDecimal.ZERO;
BigDecimal breastfeedingAbsenceMonth = BigDecimal.ZERO;
BigDecimal authorizedPaidAbsenceMonth = BigDecimal.ZERO;
BigDecimal recuperationLeaveAbsenceMonth = BigDecimal.ZERO;
BigDecimal trainingAbsenceMonth = BigDecimal.ZERO;
BigDecimal missionAbsenceMonth = BigDecimal.ZERO;
BigDecimal hospitalizationAbsenceMonth = BigDecimal.ZERO;
BigDecimal sicknessAbsenceMonth = BigDecimal.ZERO;
BigDecimal workAccidentMonth = BigDecimal.ZERO;
BigDecimal maternityMonth = BigDecimal.ZERO;
BigDecimal authorizedUnpaidAbsenceMonth = BigDecimal.ZERO;
BigDecimal annualLeaveAbsenceMonth = BigDecimal.ZERO;
BigDecimal unpaidLeaveAbsenceMonth = BigDecimal.ZERO;
BigDecimal statutoryLeaveAbsenceMonth = BigDecimal.ZERO;
BigDecimal resignationAbsenceMonth = BigDecimal.ZERO;
BigDecimal contractEndAbsenceMonth = BigDecimal.ZERO;
BigDecimal justifiedAbsenceMonth = BigDecimal.ZERO;
BigDecimal recruitmentAbsenceMonth = BigDecimal.ZERO;
BigDecimal suspensionAbsenceMonth = BigDecimal.ZERO;
BigDecimal militaryServiceAbsence = BigDecimal.ZERO;
BigDecimal irregularAbsenceMonth = BigDecimal.ZERO;
// monthlyAllowance = monthlyAllowance + remainingDateToLastOfMonth;
// Calculate totals for DailyReport
for (DailyReport dailyReport : employeeDailyReports) {
// Panier
/*LocalDate reportDate = dailyReport.getReportDate();
Integer panier = dailyReport.getPanier();
if (reportDate.isAfter(firstDayOfMonth) && panier == 1) {
monthlyAllowance = monthlyAllowance + 1;
}*/
monthlyAllowance = monthlyAllowance + dailyReport.getAllowance();
;
monthlyAllowanceRecall = monthlyAllowanceRecall + dailyReport.getAllowanceRecall();
monthlyNightHours = monthlyNightHours.add(dailyReport.getNightHours());
totalWorkHours = totalWorkHours.add(dailyReport.getWorkHours());
BigDecimal heureSup50 =
dailyReport.getExtraHours50() != null ? dailyReport.getExtraHours50() : BigDecimal.ZERO;
BigDecimal heureSup100 =
dailyReport.getExtraHours100() != null ? dailyReport.getExtraHours100() : BigDecimal.ZERO;
BigDecimal itp = dailyReport.getItp() != null ? dailyReport.getItp() : BigDecimal.ZERO;
BigDecimal nuissance =
dailyReport.getNuissance() != null ? dailyReport.getNuissance() : BigDecimal.ZERO;
BigDecimal supHours50ToDeduce =
dailyReport.getSupHours50ToDeduce() != null
? dailyReport.getSupHours50ToDeduce()
: BigDecimal.ZERO;
BigDecimal supHours100ToDeduce =
dailyReport.getSupHours100ToDeduce() != null
? dailyReport.getSupHours100ToDeduce()
: BigDecimal.ZERO;
BigDecimal itpToDeduce =
dailyReport.getItpToDeduce() != null ? dailyReport.getItpToDeduce() : BigDecimal.ZERO;
BigDecimal nuissanceToDeduce =
dailyReport.getNuissanceToDeduce() != null
? dailyReport.getNuissanceToDeduce()
: BigDecimal.ZERO;
if (dailyReport.getDeduceSupHours50() && heureSup50.compareTo(supHours50ToDeduce) > 0) {
heureSup50 = heureSup50.subtract(supHours50ToDeduce);
}
if (dailyReport.getDeduceSupHours100() && heureSup100.compareTo(supHours100ToDeduce) > 0) {
heureSup100 = heureSup100.subtract(supHours100ToDeduce);
}
if (dailyReport.getDeduceItp() && itp.compareTo(itpToDeduce) > 0) {
itp = itp.subtract(itpToDeduce);
}
if (dailyReport.getDeduceNuissance() && nuissance.compareTo(nuissanceToDeduce) > 0) {
nuissance = nuissance.subtract(nuissanceToDeduce);
}
monthlyITP = monthlyITP.add(itp);
totalNuissance = totalNuissance.add(nuissance);
// Sup Hours
if (dailyReport.getIsValidSupHours()) {
// Handle HeureSup50
if (heureSup50 != null) {
if (totalSupHours.add(heureSup50).compareTo(new BigDecimal(32)) <= 0) {
// If the total hours are still less than or equal to 32 after adding new hours
monthlyExtraHours50 = monthlyExtraHours50.add(heureSup50);
totalSupHours = totalSupHours.add(heureSup50);
} else if (totalSupHours.compareTo(new BigDecimal(32)) < 0) {
// If totalSupHours is less than 32, but adding heureSup50 exceeds 32
BigDecimal remainingHours = new BigDecimal(32).subtract(totalSupHours);
monthlyExtraHours50 = monthlyExtraHours50.add(remainingHours);
totalSupHours = totalSupHours.add(remainingHours);
// Add the excess to excessHours50
excessHours50 = excessHours50.add(heureSup50.subtract(remainingHours));
} else {
// If totalSupHours already exceeds 32, all new hours go to excess
excessHours50 = excessHours50.add(heureSup50);
}
}
// Handle HeureSup100
if (heureSup100 != null) {
if (totalSupHours.add(heureSup100).compareTo(new BigDecimal(32)) <= 0) {
// If the total hours are still less than or equal to 32 after adding new hours
monthlyExtraHours100 = monthlyExtraHours100.add(heureSup100);
totalSupHours = totalSupHours.add(heureSup100);
} else if (totalSupHours.compareTo(new BigDecimal(32)) < 0) {
// If totalSupHours is less than 32, but adding heureSup100 exceeds 32
BigDecimal remainingHours = new BigDecimal(32).subtract(totalSupHours);
monthlyExtraHours100 = monthlyExtraHours100.add(remainingHours);
totalSupHours = totalSupHours.add(remainingHours);
// Add the excess to excessHours100
excessHours100 = excessHours100.add(heureSup100.subtract(remainingHours));
} else {
// If totalSupHours already exceeds 32, all new hours go to excess
excessHours100 = excessHours100.add(heureSup100);
}
}
}
Boolean isAuthorizedAbsence = dailyReport.getIsAuthorizedAbsence();
Boolean isAuthorizedLateArrival = dailyReport.getIsAuthorizedLateArrival();
Boolean isAuthorizedEarlyDeparture = dailyReport.getIsAuthorizedEarlyDeparture();
LeaveRequest leaveRequest = dailyReport.getLeaveRequest();
Set<Absence> absences = dailyReport.getAbsenceSet();
if (dailyReport.getAbsenceHours() != null
&& dailyReport.getAbsenceHours().compareTo(BigDecimal.ZERO) > 0) {
if (isAuthorizedAbsence == false
&& isAuthorizedLateArrival == false
&& isAuthorizedEarlyDeparture == false
&& leaveRequest == null
&& absences == null) {
irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getAbsenceHours());
} else if (isAuthorizedAbsence) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getAbsenceHours());
} else if (isAuthorizedLateArrival) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getLateArrival());
} else if (isAuthorizedEarlyDeparture) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getEarlyDeparture());
} else if (leaveRequest != null) {
recuperationLeaveAbsenceMonth =
recuperationLeaveAbsenceMonth.add(dailyReport.getAbsenceHours());
} else if (absences != null) {
for (Absence absence : absences) {
totalAbsence = dailyReport.getAbsenceHours();
switch (absence.getAbsenceType()) {
case 0:
breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence);
break;
case 1:
recuperationLeaveAbsenceMonth = recuperationLeaveAbsenceMonth.add(totalAbsence);
break;
case 2:
breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence);
break;
case 3:
trainingAbsenceMonth = trainingAbsenceMonth.add(totalAbsence);
break;
case 4:
missionAbsenceMonth = missionAbsenceMonth.add(totalAbsence);
break;
case 5:
hospitalizationAbsenceMonth = hospitalizationAbsenceMonth.add(totalAbsence);
break;
case 6:
sicknessAbsenceMonth = sicknessAbsenceMonth.add(totalAbsence);
break;
case 7:
workAccidentMonth = workAccidentMonth.add(totalAbsence);
break;
case 8:
maternityMonth = maternityMonth.add(totalAbsence);
break;
case 9:
authorizedUnpaidAbsenceMonth = authorizedUnpaidAbsenceMonth.add(totalAbsence);
break;
case 10:
annualLeaveAbsenceMonth = annualLeaveAbsenceMonth.add(totalAbsence);
break;
case 11:
unpaidLeaveAbsenceMonth = unpaidLeaveAbsenceMonth.add(totalAbsence);
break;
case 12:
statutoryLeaveAbsenceMonth = statutoryLeaveAbsenceMonth.add(totalAbsence);
break;
case 13:
resignationAbsenceMonth = resignationAbsenceMonth.add(totalAbsence);
break;
case 14:
contractEndAbsenceMonth = contractEndAbsenceMonth.add(totalAbsence);
break;
case 15:
justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence);
break;
case 16:
recruitmentAbsenceMonth = recruitmentAbsenceMonth.add(totalAbsence);
break;
case 17:
suspensionAbsenceMonth = suspensionAbsenceMonth.add(totalAbsence);
break;
case 18:
militaryServiceAbsence = militaryServiceAbsence.add(totalAbsence);
break;
case 19:
if (dailyReport.getIsAuthorizedAbsence()) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence);
} else {
irregularAbsenceMonth = irregularAbsenceMonth.add(totalAbsence);
}
break;
case 20:
if (dailyReport.getIsAuthorizedLateArrival()) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getLateArrival());
} else {
irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getLateArrival());
}
break;
case 21:
if (dailyReport.getIsAuthorizedEarlyDeparture()) {
justifiedAbsenceMonth =
justifiedAbsenceMonth.add(dailyReport.getEarlyDeparture());
} else {
irregularAbsenceMonth =
irregularAbsenceMonth.add(dailyReport.getEarlyDeparture());
}
break;
default:
// Handle default case
break;
}
}
}
}
}
// Update or create MonthlyReport instance with calculated values
MonthlyReport 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);
}
public void updateMensuelReport(
MonthlyReport monthlyReport,
Employee employee,
Period period,
LocalDate startDate,
LocalDate endDate,
List<DailyReport> employeeDailyReports,
List<Absence> employeeAbsences) {
Boolean hasNuissance = employee.getHasNuissance();
LocalDate firstDayOfMonth = endDate.with(TemporalAdjusters.firstDayOfMonth());
// Perform calculations
Integer monthlyAllowance = 0;
Integer monthlyAllowanceRecall = 0;
BigDecimal monthlyNightHours = BigDecimal.ZERO;
BigDecimal monthlyExtraHours50 = BigDecimal.ZERO;
BigDecimal monthlyExtraHours100 = BigDecimal.ZERO;
BigDecimal excessHours50 = BigDecimal.ZERO;
BigDecimal excessHours100 = BigDecimal.ZERO;
BigDecimal totalSupHours = BigDecimal.ZERO;
BigDecimal monthlyITP = BigDecimal.ZERO;
BigDecimal totalNuissance = BigDecimal.ZERO;
BigDecimal totalAbsence = BigDecimal.ZERO;
BigDecimal totalWorkHours = BigDecimal.ZERO;
BigDecimal breastfeedingAbsenceMonth = BigDecimal.ZERO;
BigDecimal authorizedPaidAbsenceMonth = BigDecimal.ZERO;
BigDecimal recuperationLeaveAbsenceMonth = BigDecimal.ZERO;
BigDecimal trainingAbsenceMonth = BigDecimal.ZERO;
BigDecimal missionAbsenceMonth = BigDecimal.ZERO;
BigDecimal hospitalizationAbsenceMonth = BigDecimal.ZERO;
BigDecimal sicknessAbsenceMonth = BigDecimal.ZERO;
BigDecimal workAccidentMonth = BigDecimal.ZERO;
BigDecimal maternityMonth = BigDecimal.ZERO;
BigDecimal authorizedUnpaidAbsenceMonth = BigDecimal.ZERO;
BigDecimal annualLeaveAbsenceMonth = BigDecimal.ZERO;
BigDecimal unpaidLeaveAbsenceMonth = BigDecimal.ZERO;
BigDecimal statutoryLeaveAbsenceMonth = BigDecimal.ZERO;
BigDecimal resignationAbsenceMonth = BigDecimal.ZERO;
BigDecimal contractEndAbsenceMonth = BigDecimal.ZERO;
BigDecimal justifiedAbsenceMonth = BigDecimal.ZERO;
BigDecimal recruitmentAbsenceMonth = BigDecimal.ZERO;
BigDecimal suspensionAbsenceMonth = BigDecimal.ZERO;
BigDecimal militaryServiceAbsence = BigDecimal.ZERO;
BigDecimal irregularAbsenceMonth = BigDecimal.ZERO;
// monthlyAllowance = monthlyAllowance + remainingDateToLastOfMonth;
// Calculate totals for DailyReport
for (DailyReport dailyReport : employeeDailyReports) {
// Panier
/*LocalDate reportDate = dailyReport.getReportDate();
Integer panier = dailyReport.getPanier();
if (reportDate.isAfter(firstDayOfMonth) && panier == 1) {
monthlyAllowance = monthlyAllowance + 1;
}*/
monthlyAllowance = monthlyAllowance + dailyReport.getAllowance();
;
monthlyAllowanceRecall = monthlyAllowanceRecall + dailyReport.getAllowanceRecall();
monthlyNightHours = monthlyNightHours.add(dailyReport.getNightHours());
totalWorkHours = totalWorkHours.add(dailyReport.getWorkHours());
BigDecimal heureSup50 =
dailyReport.getExtraHours50() != null ? dailyReport.getExtraHours50() : BigDecimal.ZERO;
BigDecimal heureSup100 =
dailyReport.getExtraHours100() != null ? dailyReport.getExtraHours100() : BigDecimal.ZERO;
BigDecimal itp = dailyReport.getItp() != null ? dailyReport.getItp() : BigDecimal.ZERO;
BigDecimal nuissance =
dailyReport.getNuissance() != null ? dailyReport.getNuissance() : BigDecimal.ZERO;
BigDecimal supHours50ToDeduce =
dailyReport.getSupHours50ToDeduce() != null
? dailyReport.getSupHours50ToDeduce()
: BigDecimal.ZERO;
BigDecimal supHours100ToDeduce =
dailyReport.getSupHours100ToDeduce() != null
? dailyReport.getSupHours100ToDeduce()
: BigDecimal.ZERO;
BigDecimal itpToDeduce =
dailyReport.getItpToDeduce() != null ? dailyReport.getItpToDeduce() : BigDecimal.ZERO;
BigDecimal nuissanceToDeduce =
dailyReport.getNuissanceToDeduce() != null
? dailyReport.getNuissanceToDeduce()
: BigDecimal.ZERO;
if (dailyReport.getDeduceSupHours50() && heureSup50.compareTo(supHours50ToDeduce) > 0) {
heureSup50 = heureSup50.subtract(supHours50ToDeduce);
}
if (dailyReport.getDeduceSupHours100() && heureSup100.compareTo(supHours100ToDeduce) > 0) {
heureSup100 = heureSup100.subtract(supHours100ToDeduce);
}
if (dailyReport.getDeduceItp() && itp.compareTo(itpToDeduce) > 0) {
itp = itp.subtract(itpToDeduce);
}
if (dailyReport.getDeduceNuissance() && nuissance.compareTo(nuissanceToDeduce) > 0) {
nuissance = nuissance.subtract(nuissanceToDeduce);
}
monthlyITP = monthlyITP.add(itp);
totalNuissance = totalNuissance.add(nuissance);
// Sup Hours
if (dailyReport.getIsValidSupHours()) {
// Handle HeureSup50
if (heureSup50 != null) {
if (totalSupHours.add(heureSup50).compareTo(new BigDecimal(32)) <= 0) {
// If the total hours are still less than or equal to 32 after adding new hours
monthlyExtraHours50 = monthlyExtraHours50.add(heureSup50);
totalSupHours = totalSupHours.add(heureSup50);
} else if (totalSupHours.compareTo(new BigDecimal(32)) < 0) {
// If totalSupHours is less than 32, but adding heureSup50 exceeds 32
BigDecimal remainingHours = new BigDecimal(32).subtract(totalSupHours);
monthlyExtraHours50 = monthlyExtraHours50.add(remainingHours);
totalSupHours = totalSupHours.add(remainingHours);
// Add the excess to excessHours50
excessHours50 = excessHours50.add(heureSup50.subtract(remainingHours));
} else {
// If totalSupHours already exceeds 32, all new hours go to excess
excessHours50 = excessHours50.add(heureSup50);
}
}
// Handle HeureSup100
if (heureSup100 != null) {
if (totalSupHours.add(heureSup100).compareTo(new BigDecimal(32)) <= 0) {
// If the total hours are still less than or equal to 32 after adding new hours
monthlyExtraHours100 = monthlyExtraHours100.add(heureSup100);
totalSupHours = totalSupHours.add(heureSup100);
} else if (totalSupHours.compareTo(new BigDecimal(32)) < 0) {
// If totalSupHours is less than 32, but adding heureSup100 exceeds 32
BigDecimal remainingHours = new BigDecimal(32).subtract(totalSupHours);
monthlyExtraHours100 = monthlyExtraHours100.add(remainingHours);
totalSupHours = totalSupHours.add(remainingHours);
// Add the excess to excessHours100
excessHours100 = excessHours100.add(heureSup100.subtract(remainingHours));
} else {
// If totalSupHours already exceeds 32, all new hours go to excess
excessHours100 = excessHours100.add(heureSup100);
}
}
}
Boolean isAuthorizedAbsence = dailyReport.getIsAuthorizedAbsence();
Boolean isAuthorizedLateArrival = dailyReport.getIsAuthorizedLateArrival();
Boolean isAuthorizedEarlyDeparture = dailyReport.getIsAuthorizedEarlyDeparture();
LeaveRequest leaveRequest = dailyReport.getLeaveRequest();
Set<Absence> absences = dailyReport.getAbsenceSet();
if (dailyReport.getAbsenceHours() != null
&& dailyReport.getAbsenceHours().compareTo(BigDecimal.ZERO) > 0) {
if (isAuthorizedAbsence == false
&& isAuthorizedLateArrival == false
&& isAuthorizedEarlyDeparture == false
&& leaveRequest == null
&& absences == null) {
irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getAbsenceHours());
} else if (isAuthorizedAbsence) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getAbsenceHours());
} else if (isAuthorizedLateArrival) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getLateArrival());
} else if (isAuthorizedEarlyDeparture) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getEarlyDeparture());
} else if (leaveRequest != null) {
recuperationLeaveAbsenceMonth =
recuperationLeaveAbsenceMonth.add(dailyReport.getAbsenceHours());
} else if (absences != null) {
for (Absence absence : absences) {
totalAbsence = dailyReport.getAbsenceHours();
switch (absence.getAbsenceType()) {
case 0:
breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence);
break;
case 1:
recuperationLeaveAbsenceMonth = recuperationLeaveAbsenceMonth.add(totalAbsence);
break;
case 2:
breastfeedingAbsenceMonth = breastfeedingAbsenceMonth.add(totalAbsence);
break;
case 3:
trainingAbsenceMonth = trainingAbsenceMonth.add(totalAbsence);
break;
case 4:
missionAbsenceMonth = missionAbsenceMonth.add(totalAbsence);
break;
case 5:
hospitalizationAbsenceMonth = hospitalizationAbsenceMonth.add(totalAbsence);
break;
case 6:
sicknessAbsenceMonth = sicknessAbsenceMonth.add(totalAbsence);
break;
case 7:
workAccidentMonth = workAccidentMonth.add(totalAbsence);
break;
case 8:
maternityMonth = maternityMonth.add(totalAbsence);
break;
case 9:
authorizedUnpaidAbsenceMonth = authorizedUnpaidAbsenceMonth.add(totalAbsence);
break;
case 10:
annualLeaveAbsenceMonth = annualLeaveAbsenceMonth.add(totalAbsence);
break;
case 11:
unpaidLeaveAbsenceMonth = unpaidLeaveAbsenceMonth.add(totalAbsence);
break;
case 12:
statutoryLeaveAbsenceMonth = statutoryLeaveAbsenceMonth.add(totalAbsence);
break;
case 13:
resignationAbsenceMonth = resignationAbsenceMonth.add(totalAbsence);
break;
case 14:
contractEndAbsenceMonth = contractEndAbsenceMonth.add(totalAbsence);
break;
case 15:
justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence);
break;
case 16:
recruitmentAbsenceMonth = recruitmentAbsenceMonth.add(totalAbsence);
break;
case 17:
suspensionAbsenceMonth = suspensionAbsenceMonth.add(totalAbsence);
break;
case 18:
militaryServiceAbsence = militaryServiceAbsence.add(totalAbsence);
break;
case 19:
if (dailyReport.getIsAuthorizedAbsence()) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(totalAbsence);
} else {
irregularAbsenceMonth = irregularAbsenceMonth.add(totalAbsence);
}
break;
case 20:
if (dailyReport.getIsAuthorizedLateArrival()) {
justifiedAbsenceMonth = justifiedAbsenceMonth.add(dailyReport.getLateArrival());
} else {
irregularAbsenceMonth = irregularAbsenceMonth.add(dailyReport.getLateArrival());
}
break;
case 21:
if (dailyReport.getIsAuthorizedEarlyDeparture()) {
justifiedAbsenceMonth =
justifiedAbsenceMonth.add(dailyReport.getEarlyDeparture());
} else {
irregularAbsenceMonth =
irregularAbsenceMonth.add(dailyReport.getEarlyDeparture());
}
break;
default:
// Handle default case
break;
}
}
}
}
}
// Update or create MonthlyReport instance with calculated values
monthlyReport.setPeriod(period);
monthlyReport.setEmployee(employee);
monthlyReport.setMonthlyAllowance(monthlyAllowance);
monthlyReport.setMonthlyAllowanceRecall(monthlyAllowanceRecall);
monthlyReport.setMonthlyNightHours(monthlyNightHours);
monthlyReport.setMonthlyExtraHours50(monthlyExtraHours50);
monthlyReport.setMonthlyExtraHours100(monthlyExtraHours100);
monthlyReport.setExcessHours50(excessHours50);
monthlyReport.setExcessHours100(excessHours100);
monthlyReport.setMonthlyITP(monthlyITP);
monthlyReport.setTotalNuissance(totalNuissance);
monthlyReport.setTotalWorkHours(totalWorkHours);
// Set total absence values for each specific type
monthlyReport.setBreastfeedingAbsenceMonth(breastfeedingAbsenceMonth);
monthlyReport.setAuthorizedPaidAbsenceMonth(authorizedPaidAbsenceMonth);
monthlyReport.setAuthorizedUnpaidAbsenceMonth(authorizedUnpaidAbsenceMonth);
monthlyReport.setAnnualLeaveAbsenceMonth(annualLeaveAbsenceMonth);
monthlyReport.setRecuperationLeaveAbsenceMonth(recuperationLeaveAbsenceMonth);
monthlyReport.setUnpaidLeaveAbsenceMonth(unpaidLeaveAbsenceMonth);
monthlyReport.setStatutoryLeaveAbsenceMonth(statutoryLeaveAbsenceMonth);
monthlyReport.setResignationAbsenceMonth(resignationAbsenceMonth);
monthlyReport.setContractEndAbsenceMonth(contractEndAbsenceMonth);
monthlyReport.setTrainingAbsenceMonth(trainingAbsenceMonth);
monthlyReport.setHospitalizationAbsenceMonth(hospitalizationAbsenceMonth);
monthlyReport.setMissionAbsenceMonth(missionAbsenceMonth);
monthlyReport.setSicknessAbsenceMonth(sicknessAbsenceMonth);
monthlyReport.setWorkAccidentMonth(workAccidentMonth);
monthlyReport.setMaternityMonth(maternityMonth);
monthlyReport.setJustifiedAbsenceMonth(justifiedAbsenceMonth);
monthlyReport.setRecruitmentAbsenceMonth(recruitmentAbsenceMonth);
monthlyReport.setSuspensionAbsenceMonth(suspensionAbsenceMonth);
monthlyReport.setMilitaryServiceAbsence(militaryServiceAbsence);
monthlyReport.setIrregularAbsenceMonth(irregularAbsenceMonth);
// Save the MonthlyReport instance
monthlyReportRepository.save(monthlyReport);
}
/*
@Transactional
public void calculePrimeAssiduite(MonthlyReport monthlyReport) {
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);
}
*/
}

View File

@ -0,0 +1,486 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service;
import com.axelor.app.AppSettings;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Period;
import com.axelor.apps.base.db.repo.PeriodRepository;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.base.service.weeklyplanning.WeeklyPlanningService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmployeeBonusMgtLine;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.ExtraHoursLine;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.LeaveRequest;
import com.axelor.apps.hr.db.LunchVoucherMgtLine;
import com.axelor.apps.hr.db.PayrollLeave;
import com.axelor.apps.hr.db.PayrollPreparation;
import com.axelor.apps.hr.db.repo.EmployeeBonusMgtLineRepository;
import com.axelor.apps.hr.db.repo.EmployeeBonusMgtRepository;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.apps.hr.db.repo.ExtraHoursLineRepository;
import com.axelor.apps.hr.db.repo.HrBatchRepository;
import com.axelor.apps.hr.db.repo.LeaveRequestRepository;
import com.axelor.apps.hr.db.repo.LunchVoucherMgtLineRepository;
import com.axelor.apps.hr.db.repo.PayrollPreparationRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.hr.service.leave.LeaveService;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PayrollPreparationService {
protected LeaveService leaveService;
protected LeaveRequestRepository leaveRequestRepo;
protected WeeklyPlanningService weeklyPlanningService;
@Inject protected PayrollPreparationRepository payrollPreparationRepo;
@Inject protected AppBaseService appBaseService;
@Inject HRConfigService hrConfigService;
@Inject
public PayrollPreparationService(
LeaveService leaveService,
LeaveRequestRepository leaveRequestRepo,
WeeklyPlanningService weeklyPlanningService) {
this.leaveService = leaveService;
this.leaveRequestRepo = leaveRequestRepo;
this.weeklyPlanningService = weeklyPlanningService;
}
public PayrollPreparation generateFromEmploymentContract(
PayrollPreparation payrollPreparation, EmploymentContract employmentContract) {
if (payrollPreparation.getEmployee() == null) {
payrollPreparation.setEmployee(employmentContract.getEmployee());
}
if (payrollPreparation.getCompany() == null) {
payrollPreparation.setCompany(employmentContract.getPayCompany());
}
if (payrollPreparation.getEmploymentContract() == null) {
payrollPreparation.setEmploymentContract(employmentContract);
}
payrollPreparation.setOtherCostsEmployeeSet(employmentContract.getOtherCostsEmployeeSet());
payrollPreparation.setAnnualGrossSalary(employmentContract.getAnnualGrossSalary());
return payrollPreparation;
}
public List<PayrollLeave> fillInPayrollPreparation(PayrollPreparation payrollPreparation)
throws AxelorException {
List<PayrollLeave> payrollLeaveList = fillInLeaves(payrollPreparation);
payrollPreparation.setDuration(
this.computeWorkingDaysNumber(payrollPreparation, payrollLeaveList));
payrollPreparation.setExpenseAmount(this.computeExpenseAmount(payrollPreparation));
payrollPreparation.setLunchVoucherNumber(this.computeLunchVoucherNumber(payrollPreparation));
payrollPreparation.setEmployeeBonusAmount(computeEmployeeBonusAmount(payrollPreparation));
payrollPreparation.setExtraHoursNumber(computeExtraHoursNumber(payrollPreparation));
return payrollLeaveList;
}
public List<PayrollLeave> fillInLeaves(PayrollPreparation payrollPreparation)
throws AxelorException {
List<PayrollLeave> payrollLeaveList = new ArrayList<>();
LocalDate fromDate = payrollPreparation.getPeriod().getFromDate();
LocalDate toDate = payrollPreparation.getPeriod().getToDate();
Employee employee = payrollPreparation.getEmployee();
if (employee.getWeeklyPlanning() == null) {
throw new AxelorException(
payrollPreparation,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EMPLOYEE_PLANNING),
employee.getName());
}
List<LeaveRequest> leaveRequestList =
leaveRequestRepo
.all()
.filter(
"self.statusSelect = ?4 AND self.user.employee = ?3 AND ((self.fromDateT BETWEEN ?2 AND ?1 OR self.toDateT BETWEEN ?2 AND ?1) OR (?1 BETWEEN self.fromDateT AND self.toDateT OR ?2 BETWEEN self.fromDateT AND self.toDateT))",
toDate,
fromDate,
employee,
LeaveRequestRepository.STATUS_VALIDATED)
.fetch();
for (LeaveRequest leaveRequest : leaveRequestList) {
PayrollLeave payrollLeave = new PayrollLeave();
if (leaveRequest.getFromDateT().toLocalDate().isBefore(fromDate)) {
payrollLeave.setFromDate(fromDate);
} else {
payrollLeave.setFromDate(leaveRequest.getFromDateT().toLocalDate());
}
if (leaveRequest.getToDateT().toLocalDate().isAfter(toDate)) {
payrollLeave.setToDate(toDate);
} else {
payrollLeave.setToDate(leaveRequest.getToDateT().toLocalDate());
}
payrollLeave.setDuration(
leaveService.computeLeaveDaysByLeaveRequest(fromDate, toDate, leaveRequest, employee));
payrollLeave.setLeaveReason(leaveRequest.getLeaveLine().getLeaveReason());
payrollLeave.setLeaveRequest(leaveRequest);
payrollLeaveList.add(payrollLeave);
}
return payrollLeaveList;
}
public BigDecimal computeWorkingDaysNumber(
PayrollPreparation payrollPreparation, List<PayrollLeave> payrollLeaveList) {
LocalDate fromDate = payrollPreparation.getPeriod().getFromDate();
LocalDate toDate = payrollPreparation.getPeriod().getToDate();
LocalDate itDate = LocalDate.parse(fromDate.toString(), DateTimeFormatter.ISO_DATE);
BigDecimal workingDays = BigDecimal.ZERO;
BigDecimal leaveDays = BigDecimal.ZERO;
while (!itDate.isAfter(toDate)) {
workingDays =
workingDays.add(
BigDecimal.valueOf(
weeklyPlanningService.getWorkingDayValueInDays(
payrollPreparation.getEmployee().getWeeklyPlanning(), itDate)));
itDate = itDate.plusDays(1);
}
if (payrollLeaveList != null) {
for (PayrollLeave payrollLeave : payrollLeaveList) {
workingDays = workingDays.subtract(payrollLeave.getDuration());
leaveDays = leaveDays.add(payrollLeave.getDuration());
}
}
payrollPreparation.setLeaveDuration(leaveDays);
return workingDays;
}
public BigDecimal computeExtraHoursNumber(PayrollPreparation payrollPreparation) {
LocalDate fromDate = payrollPreparation.getPeriod().getFromDate();
LocalDate toDate = payrollPreparation.getPeriod().getToDate();
BigDecimal extraHoursNumber = BigDecimal.ZERO;
for (ExtraHoursLine extraHoursLine :
Beans.get(ExtraHoursLineRepository.class)
.all()
.filter(
"self.user.employee = ?1 AND self.extraHours.statusSelect = 3 AND self.date BETWEEN ?2 AND ?3 AND (self.payrollPreparation = null OR self.payrollPreparation.id = ?4)",
payrollPreparation.getEmployee(),
fromDate,
toDate,
payrollPreparation.getId())
.fetch()) {
payrollPreparation.addExtraHoursLineListItem(extraHoursLine);
extraHoursNumber = extraHoursNumber.add(extraHoursLine.getQty());
}
return extraHoursNumber;
}
public BigDecimal computeExpenseAmount(PayrollPreparation payrollPreparation) {
BigDecimal expenseAmount = BigDecimal.ZERO;
List<Expense> expenseList =
Beans.get(ExpenseRepository.class)
.all()
.filter(
"self.user.employee = ?1 "
+ "AND self.statusSelect = ?2 "
+ "AND (self.payrollPreparation IS NULL OR self.payrollPreparation.id = ?3) "
+ "AND self.companyCbSelect = ?4 "
+ "AND self.validationDate BETWEEN ?5 AND ?6",
payrollPreparation.getEmployee(),
ExpenseRepository.STATUS_VALIDATED,
payrollPreparation.getId(),
ExpenseRepository.COMPANY_CB_PAYMENT_NO,
payrollPreparation.getPeriod().getFromDate(),
payrollPreparation.getPeriod().getToDate())
.fetch();
for (Expense expense : expenseList) {
expenseAmount = expenseAmount.add(expense.getInTaxTotal());
payrollPreparation.addExpenseListItem(expense);
}
return expenseAmount;
}
public BigDecimal computeLunchVoucherNumber(PayrollPreparation payrollPreparation) {
BigDecimal lunchVoucherNumber = BigDecimal.ZERO;
List<LunchVoucherMgtLine> lunchVoucherList =
Beans.get(LunchVoucherMgtLineRepository.class)
.all()
.filter(
"self.employee = ?1 AND self.lunchVoucherMgt.statusSelect = 3 AND (self.payrollPreparation = null OR self.payrollPreparation.id = ?2) AND self.lunchVoucherMgt.payPeriod = ?3",
payrollPreparation.getEmployee(),
payrollPreparation.getId(),
payrollPreparation.getPeriod())
.fetch();
for (LunchVoucherMgtLine lunchVoucherMgtLine : lunchVoucherList) {
lunchVoucherNumber =
lunchVoucherNumber.add(new BigDecimal(lunchVoucherMgtLine.getLunchVoucherNumber()));
lunchVoucherNumber =
lunchVoucherNumber.add(new BigDecimal(lunchVoucherMgtLine.getInAdvanceNbr()));
payrollPreparation.addLunchVoucherMgtLineListItem(lunchVoucherMgtLine);
}
return lunchVoucherNumber;
}
public BigDecimal computeEmployeeBonusAmount(PayrollPreparation payrollPreparation) {
BigDecimal employeeBonusAmount = BigDecimal.ZERO;
List<EmployeeBonusMgtLine> employeeBonusList =
Beans.get(EmployeeBonusMgtLineRepository.class)
.all()
.filter(
"self.employee = ?1"
+ " AND self.employeeBonusMgt.statusSelect = ?4"
+ " AND (self.payrollPreparation = null"
+ " OR self.payrollPreparation.id = ?2)"
+ " AND self.employeeBonusMgt.payPeriod = ?3",
payrollPreparation.getEmployee(),
payrollPreparation.getId(),
payrollPreparation.getPeriod(),
EmployeeBonusMgtRepository.STATUS_CALCULATED)
.fetch();
for (EmployeeBonusMgtLine employeeBonusMgtLine : employeeBonusList) {
payrollPreparation.addEmployeeBonusMgtLineListItem(employeeBonusMgtLine);
employeeBonusAmount = employeeBonusAmount.add(employeeBonusMgtLine.getAmount());
}
return employeeBonusAmount;
}
@Transactional(rollbackOn = {Exception.class})
public String exportSinglePayrollPreparation(PayrollPreparation payrollPreparation)
throws IOException {
List<String[]> list = new ArrayList<>();
String[] item = new String[5];
item[0] = payrollPreparation.getEmployee().getName();
item[1] = payrollPreparation.getDuration().toString();
item[2] = payrollPreparation.getLunchVoucherNumber().toString();
item[3] = payrollPreparation.getEmployeeBonusAmount().toString();
item[4] = payrollPreparation.getExtraHoursNumber().toString();
list.add(item);
String fileName = this.getPayrollPreparationExportName();
String filePath = AppSettings.get().get("file.upload.dir");
new File(filePath).mkdirs();
CsvTool.csvWriter(filePath, fileName, ';', getPayrollPreparationExportHeader(), list);
payrollPreparation.setExported(true);
payrollPreparation.setExportDate(Beans.get(AppBaseService.class).getTodayDate());
payrollPreparationRepo.save(payrollPreparation);
Path path = Paths.get(filePath + System.getProperty("file.separator") + fileName);
try (InputStream is = new FileInputStream(path.toFile())) {
Beans.get(MetaFiles.class).attach(is, fileName, payrollPreparation);
}
return filePath + System.getProperty("file.separator") + fileName;
}
public String[] createExportFileLine(PayrollPreparation payrollPreparation) {
String[] item = new String[7];
item[0] = payrollPreparation.getEmployee().getExportCode();
item[1] = payrollPreparation.getEmployee().getContactPartner().getName();
item[2] = payrollPreparation.getEmployee().getContactPartner().getFirstName();
return item;
}
public String exportNibelisPayrollPreparation(PayrollPreparation payrollPreparation)
throws AxelorException, IOException {
List<String[]> list = new ArrayList<>();
exportNibelis(payrollPreparation, list);
String fileName = this.getPayrollPreparationExportName();
String filePath = AppSettings.get().get("file.upload.dir");
new File(filePath).mkdirs();
CsvTool.csvWriter(
filePath, fileName, ';', getPayrollPreparationMeilleurGestionExportHeader(), list);
Path path = Paths.get(filePath + System.getProperty("file.separator") + fileName);
try (InputStream is = new FileInputStream(path.toFile())) {
Beans.get(MetaFiles.class).attach(is, fileName, payrollPreparation);
}
return filePath + System.getProperty("file.separator") + fileName;
}
@Transactional(rollbackOn = {Exception.class})
public void exportNibelis(PayrollPreparation payrollPreparation, List<String[]> list)
throws AxelorException {
HRConfig hrConfig = hrConfigService.getHRConfig(payrollPreparation.getCompany());
// LEAVES
if (payrollPreparation.getLeaveDuration().compareTo(BigDecimal.ZERO) > 0) {
List<PayrollLeave> payrollLeaveList = fillInLeaves(payrollPreparation);
for (PayrollLeave payrollLeave : payrollLeaveList) {
if (payrollLeave.getLeaveReason().getPayrollPreprationExport()) {
String[] leaveLine = createExportFileLine(payrollPreparation);
leaveLine[3] = payrollLeave.getLeaveReason().getExportCode();
leaveLine[4] =
payrollLeave.getFromDate().format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
leaveLine[5] = payrollLeave.getToDate().format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
leaveLine[6] = payrollLeave.getDuration().toString();
list.add(leaveLine);
}
}
}
// LUNCH VOUCHER MANAGEMENT
if (payrollPreparation.getLunchVoucherNumber().compareTo(BigDecimal.ZERO) > 0) {
String[] lunchVoucherLine = createExportFileLine(payrollPreparation);
lunchVoucherLine[3] = hrConfig.getExportCodeForLunchVoucherManagement();
lunchVoucherLine[6] = payrollPreparation.getLunchVoucherNumber().toString();
list.add(lunchVoucherLine);
}
// EMPLOYEE BONUS MANAGEMENT
if (payrollPreparation.getEmployeeBonusAmount().compareTo(BigDecimal.ZERO) > 0) {
Map<String, BigDecimal> map = new HashMap<>();
for (EmployeeBonusMgtLine bonus : payrollPreparation.getEmployeeBonusMgtLineList()) {
if (bonus.getEmployeeBonusMgt().getEmployeeBonusType().getPayrollPreparationExport()) {
if (map.containsKey(bonus.getEmployeeBonusMgt().getEmployeeBonusType().getExportCode())) {
map.put(
bonus.getEmployeeBonusMgt().getEmployeeBonusType().getExportCode(),
bonus
.getAmount()
.add(
map.get(
bonus.getEmployeeBonusMgt().getEmployeeBonusType().getExportCode())));
} else {
map.put(
bonus.getEmployeeBonusMgt().getEmployeeBonusType().getExportCode(),
bonus.getAmount());
}
}
}
for (Map.Entry<String, BigDecimal> entry : map.entrySet()) {
String[] employeeBonusLine = createExportFileLine(payrollPreparation);
employeeBonusLine[3] = entry.getKey();
employeeBonusLine[6] = entry.getValue().toString();
list.add(employeeBonusLine);
}
}
// EXTRA HOURS
if (payrollPreparation.getExtraHoursNumber().compareTo(BigDecimal.ZERO) > 0) {
String[] extraHourLine = createExportFileLine(payrollPreparation);
extraHourLine[3] = hrConfig.getExportCodeForLunchVoucherManagement();
extraHourLine[6] = payrollPreparation.getExtraHoursNumber().toString();
list.add(extraHourLine);
}
payrollPreparation.setExported(true);
payrollPreparation.setExportDate(appBaseService.getTodayDate());
payrollPreparation.setExportTypeSelect(HrBatchRepository.EXPORT_TYPE_NIBELIS);
payrollPreparationRepo.save(payrollPreparation);
}
public String getPayrollPreparationExportName() {
return I18n.get("Payroll preparation")
+ " - "
+ Beans.get(AppBaseService.class).getTodayDateTime().toLocalDateTime().toString()
+ ".csv";
}
public String[] getPayrollPreparationExportHeader() {
String[] headers = new String[5];
headers[0] = I18n.get("Employee");
headers[1] = I18n.get("Working days' number");
headers[2] = I18n.get("Lunch vouchers' number");
headers[3] = I18n.get("Employee bonus amount");
headers[4] = I18n.get("Extra hours' number");
return headers;
}
public String[] getPayrollPreparationMeilleurGestionExportHeader() {
String[] headers = new String[7];
headers[0] = I18n.get("Registration number");
headers[1] = I18n.get("Employee lastname");
headers[2] = I18n.get("Employee firstname");
headers[3] = I18n.get("Code");
headers[4] = I18n.get("Start date");
headers[5] = I18n.get("End date");
headers[6] = I18n.get("Value");
return headers;
}
/**
* If each employee's payroll preparation has been exported, close the pay period.
*
* @param payrollPreparation
*/
@Transactional
public void closePayPeriodIfExported(PayrollPreparation payrollPreparation) {
Company company = payrollPreparation.getCompany();
Period payPeriod = payrollPreparation.getPeriod();
long nbNotExportedPayroll =
Beans.get(PayrollPreparationRepository.class)
.all()
.filter(
"self.company = :_company AND self.exported = false "
+ "AND self.period = :_period")
.bind("_company", company)
.bind("_period", payPeriod)
.count();
if (nbNotExportedPayroll == 0) {
payPeriod.setStatusSelect(PeriodRepository.STATUS_CLOSED);
payPeriod.setClosureDateTime(
Beans.get(AppBaseService.class).getTodayDateTime().toLocalDateTime());
}
Beans.get(PeriodRepository.class).save(payPeriod);
}
}

View File

@ -0,0 +1,38 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.app;
import com.axelor.apps.base.db.AppExpense;
import com.axelor.apps.base.db.AppLeave;
import com.axelor.apps.base.db.AppTimesheet;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
public interface AppHumanResourceService extends AppBaseService {
public AppTimesheet getAppTimesheet();
public AppLeave getAppLeave();
public AppExpense getAppExpense();
public void getHrmAppSettings(ActionRequest request, ActionResponse response);
public void generateHrConfigurations();
}

View File

@ -0,0 +1,111 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.app;
import com.axelor.apps.base.db.AppExpense;
import com.axelor.apps.base.db.AppLeave;
import com.axelor.apps.base.db.AppTimesheet;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.repo.AppExpenseRepository;
import com.axelor.apps.base.db.repo.AppLeaveRepository;
import com.axelor.apps.base.db.repo.AppTimesheetRepository;
import com.axelor.apps.base.db.repo.CompanyRepository;
import com.axelor.apps.base.service.app.AppBaseServiceImpl;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.repo.HRConfigRepository;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Singleton
public class AppHumanResourceServiceImpl extends AppBaseServiceImpl
implements AppHumanResourceService {
private AppTimesheetRepository appTimesheetRepo;
private AppLeaveRepository appLeaveRepo;
private AppExpenseRepository appExpenseRepo;
@Inject private CompanyRepository companyRepo;
@Inject private HRConfigRepository hrConfigRepo;
@Inject
public AppHumanResourceServiceImpl(
AppTimesheetRepository appTimesheetRepo,
AppLeaveRepository appLeaveRepo,
AppExpenseRepository appExpense) {
this.appTimesheetRepo = appTimesheetRepo;
this.appLeaveRepo = appLeaveRepo;
this.appExpenseRepo = appExpense;
}
@Override
public AppTimesheet getAppTimesheet() {
return appTimesheetRepo.all().fetchOne();
}
@Override
public AppLeave getAppLeave() {
return appLeaveRepo.all().fetchOne();
}
@Override
public AppExpense getAppExpense() {
return appExpenseRepo.all().fetchOne();
}
@Override
public void getHrmAppSettings(ActionRequest request, ActionResponse response) {
try {
Map<String, Object> map = new HashMap<>();
map.put("hasInvoicingAppEnable", isApp("invoice"));
map.put("hasLeaveAppEnable", isApp("leave"));
map.put("hasExpenseAppEnable", isApp("expense"));
map.put("hasTimesheetAppEnable", isApp("timesheet"));
map.put("hasProjectAppEnable", isApp("project"));
response.setData(map);
response.setTotal(map.size());
} catch (Exception e) {
e.printStackTrace();
response.setException(e);
}
}
@Override
@Transactional
public void generateHrConfigurations() {
List<Company> companies = companyRepo.all().filter("self.hrConfig is null").fetch();
for (Company company : companies) {
HRConfig hrConfig = new HRConfig();
hrConfig.setCompany(company);
hrConfigRepo.save(hrConfig);
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.bankorder;
import com.axelor.apps.account.db.PaymentMode;
import com.axelor.apps.account.service.invoice.InvoiceService;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderCreateService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderLineService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderService;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Currency;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.Expense;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BankOrderCreateServiceHr extends BankOrderCreateService {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Inject
public BankOrderCreateServiceHr(
BankOrderRepository bankOrderRepo,
BankOrderService bankOrderService,
BankOrderLineService bankOrderLineService,
InvoiceService invoiceService) {
super(bankOrderRepo, bankOrderService, bankOrderLineService, invoiceService);
}
/**
* Method to create a bank order for an expense
*
* @param expense An expense
* @throws AxelorException
*/
public BankOrder createBankOrder(Expense expense, BankDetails bankDetails)
throws AxelorException {
Company company = expense.getCompany();
Partner partner = expense.getUser().getPartner();
PaymentMode paymentMode = expense.getPaymentMode();
BigDecimal amount =
expense
.getInTaxTotal()
.subtract(expense.getAdvanceAmount())
.subtract(expense.getWithdrawnCash())
.subtract(expense.getPersonalExpenseAmount());
Currency currency = company.getCurrency();
LocalDate paymentDate = Beans.get(AppBaseService.class).getTodayDate();
BankOrder bankOrder =
super.createBankOrder(
paymentMode,
BankOrderRepository.PARTNER_TYPE_EMPLOYEE,
paymentDate,
company,
bankDetails,
currency,
expense.getFullName(),
expense.getFullName(),
BankOrderRepository.TECHNICAL_ORIGIN_AUTOMATIC);
bankOrder.addBankOrderLineListItem(
bankOrderLineService.createBankOrderLine(
paymentMode.getBankOrderFileFormat(),
partner,
amount,
currency,
paymentDate,
expense.getExpenseSeq(),
expense.getFullName(),
expense));
bankOrder = bankOrderRepo.save(bankOrder);
return bankOrder;
}
public BankOrder createBankOrder(Expense expense) throws AxelorException {
return createBankOrder(expense, expense.getCompany().getDefaultBankDetails());
}
}

View File

@ -0,0 +1,73 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.bankorder;
import com.axelor.apps.bankpayment.db.repo.BankOrderLineOriginRepository;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderLineOriginServiceImpl;
import com.axelor.apps.hr.db.Expense;
import com.axelor.db.Model;
import com.google.inject.Inject;
import java.time.LocalDate;
public class BankOrderLineOriginServiceHRImpl extends BankOrderLineOriginServiceImpl {
@Inject
public BankOrderLineOriginServiceHRImpl(
BankOrderLineOriginRepository bankOrderLineOriginRepository) {
super(bankOrderLineOriginRepository);
}
@Override
protected String computeRelatedToSelectName(Model model) {
if (model instanceof Expense) {
return ((Expense) model).getExpenseSeq();
} else {
return super.computeRelatedToSelectName(model);
}
}
@Override
protected LocalDate computeRelatedToSelectDate(Model model) {
if (model instanceof Expense) {
return ((Expense) model).getValidationDate();
} else {
return super.computeRelatedToSelectDate(model);
}
}
@Override
protected LocalDate computeRelatedToSelectDueDate(Model model) {
if (model instanceof Expense) {
return ((Expense) model).getValidationDate();
} else {
return super.computeRelatedToSelectDueDate(model);
}
}
}

View File

@ -0,0 +1,82 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.bankorder;
import com.axelor.apps.account.db.repo.InvoicePaymentRepository;
import com.axelor.apps.account.db.repo.InvoiceRepository;
import com.axelor.apps.account.db.repo.PaymentScheduleLineRepository;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderMergeServiceImpl;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderService;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.repo.ExpenseHRRepository;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class BankOrderMergeHRServiceImpl extends BankOrderMergeServiceImpl {
protected ExpenseHRRepository expenseHRRepository;
@Inject
public BankOrderMergeHRServiceImpl(
BankOrderRepository bankOrderRepo,
InvoicePaymentRepository invoicePaymentRepo,
BankOrderService bankOrderService,
InvoiceRepository invoiceRepository,
PaymentScheduleLineRepository paymentScheduleLineRepository,
ExpenseHRRepository expenseHRRepository) {
super(
bankOrderRepo,
invoicePaymentRepo,
bankOrderService,
invoiceRepository,
paymentScheduleLineRepository);
this.expenseHRRepository = expenseHRRepository;
}
@Transactional(rollbackOn = {AxelorException.class, Exception.class})
@Override
public BankOrder mergeBankOrders(Collection<BankOrder> bankOrders) throws AxelorException {
List<Expense> expenseList =
expenseHRRepository
.all()
.filter(
"self.bankOrder.id IN (?)",
bankOrders.stream().map(BankOrder::getId).collect(Collectors.toList()))
.fetch();
for (Expense expense : expenseList) {
expense.setBankOrder(null);
expenseHRRepository.save(expense);
}
BankOrder bankOrder = super.mergeBankOrders(bankOrders);
for (Expense expense : expenseList) {
expense.setBankOrder(bankOrder);
expenseHRRepository.save(expense);
}
return bankOrder;
}
}

View File

@ -0,0 +1,100 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.bankorder;
import com.axelor.apps.account.db.repo.InvoicePaymentRepository;
import com.axelor.apps.account.service.payment.invoice.payment.InvoicePaymentCancelService;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.db.repo.BankOrderRepository;
import com.axelor.apps.bankpayment.ebics.service.EbicsService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderLineOriginService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderLineService;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderServiceImpl;
import com.axelor.apps.bankpayment.service.config.BankPaymentConfigService;
import com.axelor.apps.base.service.administration.SequenceService;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.apps.hr.service.expense.ExpenseService;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.util.List;
public class BankOrderServiceHRImpl extends BankOrderServiceImpl {
protected ExpenseService expenseService;
@Inject
public BankOrderServiceHRImpl(
BankOrderRepository bankOrderRepo,
InvoicePaymentRepository invoicePaymentRepo,
BankOrderLineService bankOrderLineService,
EbicsService ebicsService,
InvoicePaymentCancelService invoicePaymentCancelService,
BankPaymentConfigService bankPaymentConfigService,
SequenceService sequenceService,
BankOrderLineOriginService bankOrderLineOriginService,
ExpenseService expenseService) {
super(
bankOrderRepo,
invoicePaymentRepo,
bankOrderLineService,
ebicsService,
invoicePaymentCancelService,
bankPaymentConfigService,
sequenceService,
bankOrderLineOriginService);
this.expenseService = expenseService;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void validatePayment(BankOrder bankOrder) throws AxelorException {
super.validatePayment(bankOrder);
List<Expense> expenseList =
Beans.get(ExpenseRepository.class)
.all()
.filter("self.bankOrder.id = ?", bankOrder.getId())
.fetch();
for (Expense expense : expenseList) {
if (expense != null && expense.getStatusSelect() != ExpenseRepository.STATUS_REIMBURSED) {
expense.setStatusSelect(ExpenseRepository.STATUS_REIMBURSED);
expense.setPaymentStatusSelect(InvoicePaymentRepository.STATUS_VALIDATED);
expenseService.createMoveForExpensePayment(expense);
}
}
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void cancelPayment(BankOrder bankOrder) throws AxelorException {
super.cancelPayment(bankOrder);
List<Expense> expenseList =
Beans.get(ExpenseRepository.class)
.all()
.filter("self.bankOrder.id = ?", bankOrder.getId())
.fetch();
for (Expense expense : expenseList) {
if (expense != null
&& expense.getPaymentStatusSelect() != InvoicePaymentRepository.STATUS_CANCELED) {
expenseService.cancelPayment(expense);
}
}
}
}

View File

@ -0,0 +1,213 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.account.db.AccountingBatch;
import com.axelor.apps.account.db.repo.InvoicePaymentRepository;
import com.axelor.apps.account.exception.IExceptionMessage;
import com.axelor.apps.account.service.app.AppAccountService;
import com.axelor.apps.account.service.batch.BatchCreditTransferExpensePayment;
import com.axelor.apps.bankpayment.db.BankOrder;
import com.axelor.apps.bankpayment.service.bankorder.BankOrderMergeService;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.apps.hr.service.expense.ExpenseService;
import com.axelor.db.JPA;
import com.axelor.db.Query;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.ExceptionOriginRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BatchCreditTransferExpensePaymentHR extends BatchCreditTransferExpensePayment {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final AppAccountService appAccountService;
protected final ExpenseRepository expenseRepo;
protected final ExpenseService expenseService;
protected final BankOrderMergeService bankOrderMergeService;
@Inject
public BatchCreditTransferExpensePaymentHR(
AppAccountService appAccountService,
ExpenseRepository expenseRepo,
ExpenseService expenseService,
BankOrderMergeService bankOrderMergeService) {
this.appAccountService = appAccountService;
this.expenseRepo = expenseRepo;
this.expenseService = expenseService;
this.bankOrderMergeService = bankOrderMergeService;
}
@Override
protected void process() {
List<Expense> doneList = processExpenses();
try {
mergeBankOrders(doneList);
} catch (Exception ex) {
TraceBackService.trace(ex);
ex.printStackTrace();
log.error("Credit transfer batch for expense payments: mergeBankOrders");
}
}
@Override
protected void stop() {
StringBuilder sb = new StringBuilder();
sb.append(I18n.get(IExceptionMessage.BATCH_CREDIT_TRANSFER_REPORT_TITLE)).append(" ");
sb.append(
String.format(
I18n.get(
com.axelor.apps.hr.exception.IExceptionMessage
.BATCH_CREDIT_TRANSFER_EXPENSE_DONE_SINGULAR,
com.axelor.apps.hr.exception.IExceptionMessage
.BATCH_CREDIT_TRANSFER_EXPENSE_DONE_PLURAL,
batch.getDone())
+ " ",
batch.getDone()));
sb.append(
String.format(
I18n.get(
com.axelor.apps.base.exceptions.IExceptionMessage.ABSTRACT_BATCH_ANOMALY_SINGULAR,
com.axelor.apps.base.exceptions.IExceptionMessage.ABSTRACT_BATCH_ANOMALY_PLURAL,
batch.getAnomaly()),
batch.getAnomaly()));
addComment(sb.toString());
super.stop();
}
/**
* Process expenses that need to be paid.
*
* @return
*/
protected List<Expense> processExpenses() {
List<Expense> doneList = new ArrayList<>();
List<Long> anomalyList = Lists.newArrayList(0L); // Can't pass an empty collection to the query
AccountingBatch accountingBatch = batch.getAccountingBatch();
boolean manageMultiBanks = appAccountService.getAppBase().getManageMultiBanks();
String filter =
"self.ventilated = true "
+ "AND self.paymentStatusSelect = :paymentStatusSelect "
+ "AND self.company = :company "
+ "AND self.user.partner.outPaymentMode = :paymentMode "
+ "AND self.id NOT IN (:anomalyList)";
if (manageMultiBanks) {
filter += " AND self.bankDetails IN (:bankDetailsSet)";
}
Query<Expense> query =
expenseRepo
.all()
.filter(filter)
.bind("paymentStatusSelect", InvoicePaymentRepository.STATUS_DRAFT)
.bind("company", accountingBatch.getCompany())
.bind("paymentMode", accountingBatch.getPaymentMode())
.bind("anomalyList", anomalyList);
if (manageMultiBanks) {
Set<BankDetails> bankDetailsSet = Sets.newHashSet(accountingBatch.getBankDetails());
if (accountingBatch.getIncludeOtherBankAccounts()) {
bankDetailsSet.addAll(accountingBatch.getCompany().getBankDetailsSet());
}
query.bind("bankDetailsSet", bankDetailsSet);
}
for (List<Expense> expenseList;
!(expenseList = query.fetch(FETCH_LIMIT)).isEmpty();
JPA.clear()) {
for (Expense expense : expenseList) {
try {
addPayment(expense, accountingBatch.getBankDetails());
doneList.add(expense);
incrementDone();
} catch (Exception ex) {
incrementAnomaly();
anomalyList.add(expense.getId());
query.bind("anomalyList", anomalyList);
TraceBackService.trace(ex, ExceptionOriginRepository.CREDIT_TRANSFER, batch.getId());
ex.printStackTrace();
log.error(
String.format(
"Credit transfer batch for expense payment: anomaly for expense %s",
expense.getExpenseSeq()));
break;
}
}
}
return doneList;
}
/**
* Add a payment to the specified expense.
*
* @param expense
* @param bankDetails
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
protected void addPayment(Expense expense, BankDetails bankDetails) throws AxelorException {
log.debug(
String.format(
"Credit transfer batch for expense payment: adding payment for expense %s",
expense.getExpenseSeq()));
expenseService.addPayment(expense, bankDetails);
}
/**
* Merge bank orders.
*
* @param doneList
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
protected void mergeBankOrders(List<Expense> doneList) throws AxelorException {
List<Expense> expenseList = new ArrayList<>();
List<BankOrder> bankOrderList = new ArrayList<>();
for (Expense expense : doneList) {
BankOrder bankOrder = expense.getBankOrder();
if (bankOrder != null) {
expenseList.add(expense);
bankOrderList.add(bankOrder);
}
}
if (bankOrderList.size() > 1) {
BankOrder mergedBankOrder = bankOrderMergeService.mergeBankOrders(bankOrderList);
for (Expense expense : expenseList) {
expense.setBankOrder(mergedBankOrder);
}
}
}
}

View File

@ -0,0 +1,120 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.HrBatch;
import com.axelor.apps.hr.db.repo.EmploymentContractRepository;
import com.axelor.apps.hr.db.repo.HrBatchRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.EmploymentContractService;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class BatchEmploymentContractExport extends BatchStrategy {
protected int total;
protected HrBatch hrBatch;
@Override
protected void start() throws IllegalAccessException {
super.start();
total = 0;
hrBatch = Beans.get(HrBatchRepository.class).find(batch.getHrBatch().getId());
checkPoint();
}
@Override
protected void process() {
List<EmploymentContract> employmentContractList =
Beans.get(EmploymentContractRepository.class)
.all()
.filter(
"self.payCompany = ?1 AND self.status = ?2",
hrBatch.getCompany(),
EmploymentContractRepository.STATUS_ACTIVE)
.fetch();
switch (hrBatch.getEmploymentContractExportTypeSelect()) {
case HrBatchRepository.EMPLOYMENT_CONTRACT_EXPORT_TYPE_SILAE:
try {
batch.setMetaFile(employmentContractExportSilae(employmentContractList));
} catch (IOException e) {
incrementAnomaly();
TraceBackService.trace(e);
}
break;
default:
break;
}
}
@Transactional(rollbackOn = {Exception.class})
public MetaFile employmentContractExportSilae(List<EmploymentContract> employmentContractList)
throws IOException {
List<String[]> list = new ArrayList<>();
for (EmploymentContract employmentContract : employmentContractList) {
Beans.get(EmploymentContractService.class)
.employmentContractExportSilae(employmentContract, list);
total++;
incrementDone();
}
File tempFile =
MetaFiles.createTempFile(
Beans.get(EmploymentContractService.class).employmentContractExportName(), ".csv")
.toFile();
String[] headers = Beans.get(EmploymentContractService.class).employmentContractExportHeaders();
CsvTool.csvWriter(tempFile.getParent(), tempFile.getName(), ';', headers, list);
MetaFiles metaFiles = Beans.get(MetaFiles.class);
MetaFile metaFile = metaFiles.upload(tempFile);
tempFile.delete();
return metaFile;
}
@Override
protected void stop() {
String comment =
String.format(
I18n.get(IExceptionMessage.BATCH_EMPLOYMENT_CONTRACT_EXPORT_RECAP) + '\n', total);
addComment(comment);
super.stop();
}
}

View File

@ -0,0 +1,241 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.HrBatch;
import com.axelor.apps.hr.db.LeaveLine;
import com.axelor.apps.hr.db.LeaveManagement;
import com.axelor.apps.hr.db.LeaveReason;
import com.axelor.apps.hr.db.repo.LeaveLineRepository;
import com.axelor.apps.hr.db.repo.LeaveManagementRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.leave.LeaveService;
import com.axelor.apps.hr.service.leave.management.LeaveManagementService;
import com.axelor.auth.AuthUtils;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.ExceptionOriginRepository;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import javax.validation.constraints.Digits;
public class BatchLeaveManagement extends BatchStrategy {
int total;
int noValueAnomaly;
int confAnomaly;
protected LeaveLineRepository leaveLineRepository;
protected LeaveManagementRepository leaveManagementRepository;
@Inject private Provider<LeaveService> leaveServiceProvider;
@Inject
public BatchLeaveManagement(
LeaveManagementService leaveManagementService,
LeaveLineRepository leaveLineRepository,
LeaveManagementRepository leaveManagementRepository) {
super(leaveManagementService);
this.leaveLineRepository = leaveLineRepository;
this.leaveManagementRepository = leaveManagementRepository;
}
@Override
protected void start() throws IllegalAccessException {
super.start();
if (batch.getHrBatch().getDayNumber() == null
|| batch.getHrBatch().getDayNumber().signum() == 0
|| batch.getHrBatch().getLeaveReason() == null) {
TraceBackService.trace(
new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BATCH_MISSING_FIELD)),
ExceptionOriginRepository.LEAVE_MANAGEMENT,
batch.getId());
}
total = 0;
noValueAnomaly = 0;
confAnomaly = 0;
checkPoint();
}
@Override
protected void process() {
List<Employee> employeeList = this.getEmployees(batch.getHrBatch());
generateLeaveManagementLines(employeeList);
}
public List<Employee> getEmployees(HrBatch hrBatch) {
List<String> query = Lists.newArrayList();
if (hrBatch.getEmployeeSet() != null && !hrBatch.getEmployeeSet().isEmpty()) {
String employeeIds =
Joiner.on(',')
.join(Iterables.transform(hrBatch.getEmployeeSet(), obj -> obj.getId().toString()));
query.add("self.id IN (" + employeeIds + ")");
}
if (hrBatch.getEmployeeSet() != null && !hrBatch.getPlanningSet().isEmpty()) {
String planningIds =
Joiner.on(',')
.join(Iterables.transform(hrBatch.getPlanningSet(), obj -> obj.getId().toString()));
query.add("self.weeklyPlanning.id IN (" + planningIds + ")");
}
List<Employee> employeeList;
String liaison = query.isEmpty() ? "" : " AND";
if (hrBatch.getCompany() != null) {
employeeList =
JPA.all(Employee.class)
.filter(
Joiner.on(" AND ").join(query)
+ liaison
+ " self.mainEmploymentContract.payCompany = :company")
.bind("company", hrBatch.getCompany())
.fetch();
} else {
employeeList = JPA.all(Employee.class).filter(Joiner.on(" AND ").join(query)).fetch();
}
return employeeList;
}
public void generateLeaveManagementLines(List<Employee> employeeList) {
for (Employee employee : employeeList) {
try {
createLeaveManagement(employeeRepository.find(employee.getId()));
} catch (AxelorException e) {
TraceBackService.trace(e, ExceptionOriginRepository.LEAVE_MANAGEMENT, batch.getId());
incrementAnomaly();
if (e.getCategory() == TraceBackRepository.CATEGORY_NO_VALUE) {
noValueAnomaly++;
}
if (e.getCategory() == TraceBackRepository.CATEGORY_CONFIGURATION_ERROR) {
confAnomaly++;
}
} finally {
total++;
JPA.clear();
}
}
}
@Transactional(rollbackOn = {Exception.class})
public void createLeaveManagement(Employee employee) throws AxelorException {
batch = batchRepo.find(batch.getId());
LeaveLine leaveLine = null;
LeaveReason leaveReason = batch.getHrBatch().getLeaveReason();
if (employee != null) {
leaveLine = leaveServiceProvider.get().addLeaveReasonOrCreateIt(employee, leaveReason);
BigDecimal dayNumber =
batch.getHrBatch().getUseWeeklyPlanningCoef()
? batch
.getHrBatch()
.getDayNumber()
.multiply(employee.getWeeklyPlanning().getLeaveCoef())
: batch.getHrBatch().getDayNumber();
dayNumber =
dayNumber.subtract(
new BigDecimal(
publicHolidayService.getImposedDayNumber(
employee,
batch.getHrBatch().getStartDate(),
batch.getHrBatch().getEndDate())));
LeaveManagement leaveManagement =
leaveManagementService.createLeaveManagement(
leaveLine,
AuthUtils.getUser(),
batch.getHrBatch().getComments(),
null,
batch.getHrBatch().getStartDate(),
batch.getHrBatch().getEndDate(),
dayNumber);
BigDecimal qty = leaveLine.getQuantity().add(dayNumber);
BigDecimal totalQty = leaveLine.getTotalQuantity().add(dayNumber);
try {
int integer =
LeaveLine.class.getDeclaredField("quantity").getAnnotation(Digits.class).integer();
BigDecimal limit = new BigDecimal((long) Math.pow(10, integer));
if (qty.compareTo(limit) >= 0 || totalQty.compareTo(limit) >= 0) {
throw new AxelorException(
employee,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_QTY_OUT_OF_BOUNDS),
limit.longValue());
}
} catch (NoSuchFieldException | SecurityException e) {
throw new AxelorException(e, TraceBackRepository.CATEGORY_CONFIGURATION_ERROR);
}
leaveLine.setQuantity(qty.setScale(4, RoundingMode.HALF_EVEN));
leaveLine.setTotalQuantity(totalQty.setScale(4, RoundingMode.HALF_EVEN));
leaveManagementRepository.save(leaveManagement);
leaveLineRepository.save(leaveLine);
updateEmployee(employee);
}
}
@Override
protected void stop() {
String comment =
String.format(I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_ENDING_0), total) + '\n';
comment +=
String.format(I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_ENDING_1), batch.getDone())
+ '\n';
if (confAnomaly > 0) {
comment +=
String.format(I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_ENDING_2), confAnomaly)
+ '\n';
}
if (noValueAnomaly > 0) {
comment +=
String.format(I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_ENDING_3), noValueAnomaly)
+ '\n';
}
addComment(comment);
super.stop();
}
}

View File

@ -0,0 +1,87 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.LeaveLine;
import com.axelor.apps.hr.db.LeaveReason;
import com.axelor.apps.hr.db.repo.LeaveLineRepository;
import com.axelor.apps.hr.db.repo.LeaveManagementRepository;
import com.axelor.apps.hr.service.leave.management.LeaveManagementService;
import com.axelor.auth.AuthUtils;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.ExceptionOriginRepository;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.util.List;
public class BatchLeaveManagementReset extends BatchLeaveManagement {
@Inject
public BatchLeaveManagementReset(
LeaveManagementService leaveManagementService,
LeaveLineRepository leaveLineRepository,
LeaveManagementRepository leaveManagementRepository) {
super(leaveManagementService, leaveLineRepository, leaveManagementRepository);
}
@Override
protected void process() {
List<Employee> employeeList = getEmployees(batch.getHrBatch());
resetLeaveManagementLines(employeeList);
}
public void resetLeaveManagementLines(List<Employee> employeeList) {
for (Employee employee : employeeList) {
try {
resetLeaveManagement(employeeRepository.find(employee.getId()));
} catch (AxelorException e) {
TraceBackService.trace(e, ExceptionOriginRepository.LEAVE_MANAGEMENT, batch.getId());
incrementAnomaly();
if (e.getCategory() == TraceBackRepository.CATEGORY_NO_VALUE) {
noValueAnomaly++;
}
if (e.getCategory() == TraceBackRepository.CATEGORY_CONFIGURATION_ERROR) {
confAnomaly++;
}
} finally {
total++;
JPA.clear();
}
}
}
@Transactional(rollbackOn = {Exception.class})
public void resetLeaveManagement(Employee employee) throws AxelorException {
LeaveReason leaveReason = batch.getHrBatch().getLeaveReason();
for (LeaveLine leaveLine : employee.getLeaveLineList()) {
if (leaveReason.equals(leaveLine.getLeaveReason())) {
leaveManagementService.reset(
leaveLine,
AuthUtils.getUser(),
batch.getHrBatch().getComments(),
null,
batch.getHrBatch().getStartDate(),
batch.getHrBatch().getEndDate());
}
}
}
}

View File

@ -0,0 +1,205 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.app.AppSettings;
import com.axelor.apps.base.db.repo.CompanyRepository;
import com.axelor.apps.base.db.repo.PeriodRepository;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.HrBatch;
import com.axelor.apps.hr.db.PayrollPreparation;
import com.axelor.apps.hr.db.repo.HrBatchRepository;
import com.axelor.apps.hr.db.repo.PayrollPreparationRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.PayrollPreparationService;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.ExceptionOriginRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.repo.MetaFileRepository;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class BatchPayrollPreparationExport extends BatchStrategy {
protected int total;
protected HrBatch hrBatch;
protected PayrollPreparationService payrollPreparationService;
@Inject protected PayrollPreparationRepository payrollPreparationRepository;
@Inject CompanyRepository companyRepository;
@Inject PeriodRepository periodRepository;
@Inject HRConfigService hrConfigService;
@Inject
public BatchPayrollPreparationExport(PayrollPreparationService payrollPreparationService) {
super();
this.payrollPreparationService = payrollPreparationService;
}
@Override
protected void start() throws IllegalAccessException {
super.start();
total = 0;
hrBatch = Beans.get(HrBatchRepository.class).find(batch.getHrBatch().getId());
checkPoint();
}
@Override
protected void process() {
String exportAll = "";
if (!hrBatch.getExportAlreadyExported()) {
exportAll = " AND self.exported = false ";
}
List<PayrollPreparation> payrollPreparationList =
payrollPreparationRepository
.all()
.filter(
"self.company = ?1 AND self.period = ?2 " + exportAll,
hrBatch.getCompany(),
hrBatch.getPeriod())
.fetch();
switch (hrBatch.getPayrollPreparationExportTypeSelect()) {
case HrBatchRepository.EXPORT_TYPE_STANDARD:
try {
batch.setMetaFile(standardExport(payrollPreparationList));
} catch (IOException e) {
incrementAnomaly();
TraceBackService.trace(e, ExceptionOriginRepository.LEAVE_MANAGEMENT, batch.getId());
}
break;
case HrBatchRepository.EXPORT_TYPE_NIBELIS:
try {
batch.setMetaFile(nibelisExport(payrollPreparationList));
} catch (Exception e) {
incrementAnomaly();
TraceBackService.trace(e, ExceptionOriginRepository.LEAVE_MANAGEMENT, batch.getId());
}
break;
default:
break;
}
}
@Transactional(rollbackOn = {Exception.class})
public MetaFile standardExport(List<PayrollPreparation> payrollPreparationList)
throws IOException {
List<String[]> list = new ArrayList<>();
LocalDate today = Beans.get(AppBaseService.class).getTodayDate();
for (PayrollPreparation payrollPreparation : payrollPreparationList) {
String[] item = new String[5];
item[0] = payrollPreparation.getEmployee().getName();
item[1] = payrollPreparation.getDuration().toString();
item[2] = payrollPreparation.getLunchVoucherNumber().toString();
item[3] = payrollPreparation.getEmployeeBonusAmount().toString();
item[4] = payrollPreparation.getExtraHoursNumber().toString();
list.add(item);
payrollPreparation.setExported(true);
payrollPreparation.setExportDate(today);
payrollPreparation.setExportTypeSelect(HrBatchRepository.EXPORT_TYPE_STANDARD);
payrollPreparation.addBatchListItem(batch);
payrollPreparationRepository.save(payrollPreparation);
total++;
incrementDone();
}
String fileName = Beans.get(PayrollPreparationService.class).getPayrollPreparationExportName();
String filePath = AppSettings.get().get("file.upload.dir");
MetaFile metaFile = new MetaFile();
metaFile.setFileName(fileName);
metaFile.setFilePath(fileName);
metaFile = Beans.get(MetaFileRepository.class).save(metaFile);
new File(filePath).mkdirs();
CsvTool.csvWriter(
filePath,
fileName,
';',
Beans.get(PayrollPreparationService.class).getPayrollPreparationExportHeader(),
list);
return metaFile;
}
@Transactional(rollbackOn = {Exception.class})
public MetaFile nibelisExport(List<PayrollPreparation> payrollPreparationList)
throws IOException, AxelorException {
List<String[]> list = new ArrayList<>();
for (PayrollPreparation payrollPreparation : payrollPreparationList) {
payrollPreparation.addBatchListItem(batch);
payrollPreparationService.exportNibelis(payrollPreparation, list);
total++;
}
String fileName = Beans.get(PayrollPreparationService.class).getPayrollPreparationExportName();
String filePath = AppSettings.get().get("file.upload.dir");
MetaFile metaFile = new MetaFile();
metaFile.setFileName(fileName);
metaFile.setFilePath(fileName);
metaFile = Beans.get(MetaFileRepository.class).save(metaFile);
new File(filePath).mkdirs();
CsvTool.csvWriter(
filePath,
fileName,
';',
Beans.get(PayrollPreparationService.class)
.getPayrollPreparationMeilleurGestionExportHeader(),
list);
return metaFile;
}
@Override
protected void stop() {
String comment =
String.format(
I18n.get(IExceptionMessage.BATCH_PAYROLL_PREPARATION_EXPORT_RECAP) + '\n', total);
addComment(comment);
super.stop();
}
}

View File

@ -0,0 +1,219 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Period;
import com.axelor.apps.base.db.repo.CompanyRepository;
import com.axelor.apps.base.db.repo.PeriodRepository;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.HrBatch;
import com.axelor.apps.hr.db.PayrollPreparation;
import com.axelor.apps.hr.db.repo.EmploymentContractRepository;
import com.axelor.apps.hr.db.repo.HrBatchRepository;
import com.axelor.apps.hr.db.repo.PayrollPreparationRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.PayrollPreparationService;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.ExceptionOriginRepository;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BatchPayrollPreparationGeneration extends BatchStrategy {
protected final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected int duplicateAnomaly;
protected int configurationAnomaly;
protected int total;
protected HrBatch hrBatch;
protected Company company;
protected PayrollPreparationService payrollPreparationService;
@Inject protected PayrollPreparationRepository payrollPreparationRepository;
@Inject protected CompanyRepository companyRepository;
@Inject protected PeriodRepository periodRepository;
@Inject
public BatchPayrollPreparationGeneration(PayrollPreparationService payrollPreparationService) {
super();
this.payrollPreparationService = payrollPreparationService;
}
@Override
protected void start() throws IllegalAccessException {
super.start();
duplicateAnomaly = 0;
configurationAnomaly = 0;
total = 0;
hrBatch = Beans.get(HrBatchRepository.class).find(batch.getHrBatch().getId());
if (hrBatch.getCompany() != null) {
company = Beans.get(CompanyRepository.class).find(hrBatch.getCompany().getId());
}
checkPoint();
}
@Override
protected void process() {
List<Employee> employeeList = this.getEmployees(hrBatch);
generatePayrollPreparations(employeeList);
}
public List<Employee> getEmployees(HrBatch hrBatch) {
List<String> query = Lists.newArrayList();
if (!hrBatch.getEmployeeSet().isEmpty()) {
String employeeIds =
Joiner.on(',')
.join(Iterables.transform(hrBatch.getEmployeeSet(), obj -> obj.getId().toString()));
query.add("self.id IN (" + employeeIds + ")");
}
if (!hrBatch.getPlanningSet().isEmpty()) {
String planningIds =
Joiner.on(',')
.join(Iterables.transform(hrBatch.getPlanningSet(), obj -> obj.getId().toString()));
query.add("self.weeklyPlanning.id IN (" + planningIds + ")");
}
String liaison = query.isEmpty() ? "" : " AND";
if (hrBatch.getCompany() != null) {
return JPA.all(Employee.class)
.filter(
Joiner.on(" AND ").join(query)
+ liaison
+ " self.mainEmploymentContract.payCompany = :company")
.bind("company", hrBatch.getCompany())
.fetch();
} else {
return JPA.all(Employee.class).filter(Joiner.on(" AND ").join(query)).fetch();
}
}
public void generatePayrollPreparations(List<Employee> employeeList) {
for (Employee employee : employeeList) {
try {
employee = employeeRepository.find(employee.getId());
if (employee.getMainEmploymentContract() != null
&& employee.getMainEmploymentContract().getStatus()
!= EmploymentContractRepository.STATUS_CLOSED) {
createPayrollPreparation(employee);
}
} catch (AxelorException e) {
TraceBackService.trace(e, ExceptionOriginRepository.LEAVE_MANAGEMENT, batch.getId());
incrementAnomaly();
if (e.getCategory() == TraceBackRepository.CATEGORY_NO_UNIQUE_KEY) {
duplicateAnomaly++;
} else if (e.getCategory() == TraceBackRepository.CATEGORY_CONFIGURATION_ERROR) {
configurationAnomaly++;
}
} finally {
total++;
JPA.clear();
}
}
}
@Transactional(rollbackOn = {Exception.class})
public void createPayrollPreparation(Employee employee) throws AxelorException {
String filter = "self.period = ?1 AND self.employee = ?2";
String companyFilter = filter + " AND self.company = ?3";
List<PayrollPreparation> payrollPreparationList =
payrollPreparationRepository
.all()
.filter(
(company != null) ? companyFilter : filter, hrBatch.getPeriod(), employee, company)
.fetch();
log.debug("list : " + payrollPreparationList);
if (!payrollPreparationList.isEmpty()) {
throw new AxelorException(
employee,
TraceBackRepository.CATEGORY_NO_UNIQUE_KEY,
I18n.get(IExceptionMessage.PAYROLL_PREPARATION_DUPLICATE),
employee.getName(),
(company != null) ? hrBatch.getCompany().getName() : null,
hrBatch.getPeriod().getName());
}
PayrollPreparation payrollPreparation = new PayrollPreparation();
if (company != null) {
Company currentCompany = companyRepository.find(company.getId());
payrollPreparation.setCompany(currentCompany);
} else {
payrollPreparation.setCompany(employee.getMainEmploymentContract().getPayCompany());
}
Period period = periodRepository.find(hrBatch.getPeriod().getId());
payrollPreparation.setEmployee(employee);
payrollPreparation.setEmploymentContract(employee.getMainEmploymentContract());
payrollPreparation.setPeriod(period);
payrollPreparationService.fillInPayrollPreparation(payrollPreparation);
payrollPreparationRepository.save(payrollPreparation);
updateEmployee(employee);
}
@Override
protected void stop() {
String comment =
String.format(
I18n.get(IExceptionMessage.BATCH_PAYROLL_PREPARATION_GENERATION_RECAP) + '\n', total);
comment +=
String.format(
I18n.get(IExceptionMessage.BATCH_PAYROLL_PREPARATION_SUCCESS_RECAP) + '\n',
batch.getDone());
if (duplicateAnomaly > 0) {
comment +=
String.format(
I18n.get(IExceptionMessage.BATCH_PAYROLL_PREPARATION_DUPLICATE_RECAP) + '\n',
duplicateAnomaly);
}
if (configurationAnomaly > 0) {
comment +=
String.format(
I18n.get(IExceptionMessage.BATCH_PAYROLL_PREPARATION_CONFIGURATION_RECAP) + '\n',
configurationAnomaly);
}
addComment(comment);
super.stop();
}
}

View File

@ -0,0 +1,288 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
/**
* Axelor Business Solutions
*
* <p>Copyright (C) 2016 Axelor (<http://axelor.com>).
*
* <p>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.
*
* <p>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.
*
* <p>You should have received a copy of the GNU Affero General Public License along with this
* program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.HrBatch;
import com.axelor.apps.hr.db.LeaveLine;
import com.axelor.apps.hr.db.LeaveManagement;
import com.axelor.apps.hr.db.LeaveManagementBatchRule;
import com.axelor.apps.hr.db.repo.HRConfigRepository;
import com.axelor.apps.hr.db.repo.LeaveLineRepository;
import com.axelor.apps.hr.db.repo.LeaveManagementRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.employee.EmployeeService;
import com.axelor.apps.hr.service.leave.management.LeaveManagementService;
import com.axelor.auth.AuthUtils;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.ExceptionOriginRepository;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.tool.template.TemplateMaker;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
public class BatchSeniorityLeaveManagement extends BatchStrategy {
int total;
int noValueAnomaly;
int confAnomaly;
private static final char TEMPLATE_DELIMITER = '$';
protected TemplateMaker maker;
protected HRConfig hrConfig;
protected LeaveLineRepository leaveLineRepository;
protected LeaveManagementRepository leaveManagementRepository;
@Inject
public BatchSeniorityLeaveManagement(
LeaveManagementService leaveManagementService,
LeaveLineRepository leaveLineRepository,
LeaveManagementRepository leaveManagementRepository) {
super(leaveManagementService);
this.leaveLineRepository = leaveLineRepository;
this.leaveManagementRepository = leaveManagementRepository;
}
@Override
protected void start() throws IllegalAccessException {
super.start();
if (batch.getHrBatch().getDayNumber() == null
|| batch.getHrBatch().getDayNumber() == BigDecimal.ZERO
|| batch.getHrBatch().getLeaveReason() == null)
TraceBackService.trace(
new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.BATCH_MISSING_FIELD)),
ExceptionOriginRepository.LEAVE_MANAGEMENT,
batch.getId());
total = 0;
noValueAnomaly = 0;
confAnomaly = 0;
this.maker = new TemplateMaker(Locale.FRENCH, TEMPLATE_DELIMITER, TEMPLATE_DELIMITER);
hrConfig =
Beans.get(HRConfigRepository.class)
.all()
.filter("self.company.id = ?1 ", batch.getHrBatch().getCompany().getId())
.fetchOne();
checkPoint();
}
@Override
protected void process() {
List<Employee> employeeList = this.getEmployees(batch.getHrBatch());
generateLeaveManagementLines(employeeList);
}
public List<Employee> getEmployees(HrBatch hrBatch) {
List<Employee> employeeList;
if (hrBatch.getCompany() != null) {
employeeList =
JPA.all(Employee.class)
.filter("self.mainEmploymentContract.payCompany = :company")
.bind("company", hrBatch.getCompany())
.fetch();
} else {
employeeList = JPA.all(Employee.class).fetch();
}
return employeeList;
}
public void generateLeaveManagementLines(List<Employee> employeeList) {
for (Employee employee : employeeList) {
try {
createLeaveManagement(employeeRepository.find(employee.getId()));
} catch (AxelorException e) {
TraceBackService.trace(e, ExceptionOriginRepository.LEAVE_MANAGEMENT, batch.getId());
incrementAnomaly();
if (e.getCategory() == TraceBackRepository.CATEGORY_NO_VALUE) {
noValueAnomaly++;
}
if (e.getCategory() == TraceBackRepository.CATEGORY_CONFIGURATION_ERROR) {
confAnomaly++;
}
} finally {
total++;
JPA.clear();
}
}
}
@Transactional(rollbackOn = {Exception.class})
public void createLeaveManagement(Employee employee) throws AxelorException {
batch = batchRepo.find(batch.getId());
int count = 0;
String eval = null;
LeaveLine leaveLine = null;
BigDecimal quantity = BigDecimal.ZERO;
if (!employee.getLeaveLineList().isEmpty()) {
for (LeaveLine line : employee.getLeaveLineList()) {
if (line.getLeaveReason().equals(batch.getHrBatch().getLeaveReason())) {
count++;
leaveLine = line;
}
}
}
if (count == 0) {
throw new AxelorException(
employee,
TraceBackRepository.CATEGORY_NO_VALUE,
I18n.get(IExceptionMessage.EMPLOYEE_NO_LEAVE_MANAGEMENT),
employee.getName(),
batch.getHrBatch().getLeaveReason().getLeaveReason());
}
if (count > 1) {
throw new AxelorException(
employee,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EMPLOYEE_DOUBLE_LEAVE_MANAGEMENT),
employee.getName(),
batch.getHrBatch().getLeaveReason().getLeaveReason());
}
if (count == 1) {
EmploymentContract contract = employee.getMainEmploymentContract();
if (contract == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_NO_VALUE,
IExceptionMessage.EMPLOYEE_CONTRACT_OF_EMPLOYMENT);
}
Integer executiveStatusSelect = contract.getExecutiveStatusSelect();
for (LeaveManagementBatchRule rule :
Beans.get(HRConfigRepository.class)
.all()
.filter("self.company.id = ?1", batch.getHrBatch().getCompany().getId())
.fetchOne()
.getLeaveManagementBatchRuleList()) {
if (rule.getExecutiveStatusSelect().equals(executiveStatusSelect)) {
maker.setContext(employee, "Employee");
String formula = rule.getFormula();
formula =
formula.replace(
hrConfig.getSeniorityVariableName(),
String.valueOf(
Beans.get(EmployeeService.class)
.getLengthOfService(employee, batch.getHrBatch().getReferentialDate())));
formula =
formula.replace(
hrConfig.getAgeVariableName(),
String.valueOf(
Beans.get(EmployeeService.class)
.getAge(employee, batch.getHrBatch().getReferentialDate())));
maker.setTemplate(formula);
eval = maker.make();
CompilerConfiguration conf = new CompilerConfiguration();
ImportCustomizer customizer = new ImportCustomizer();
customizer.addStaticStars("java.lang.Math");
conf.addCompilationCustomizers(customizer);
Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding, conf);
if (shell.evaluate(eval).toString().equals("true")) {
quantity = rule.getLeaveDayNumber();
break;
}
}
}
if (quantity.signum() == 0) {
// If the quantity equals 0, no need to create a leaveManagement and to update the employee,
// since we won't give them any leaves
incrementDone();
return;
}
LeaveManagement leaveManagement =
leaveManagementService.createLeaveManagement(
leaveLine,
AuthUtils.getUser(),
batch.getHrBatch().getComments(),
null,
batch.getHrBatch().getStartDate(),
batch.getHrBatch().getEndDate(),
quantity);
leaveLine.setQuantity(leaveLine.getQuantity().add(quantity));
leaveLine.setTotalQuantity(leaveLine.getTotalQuantity().add(quantity));
leaveManagementRepository.save(leaveManagement);
leaveLineRepository.save(leaveLine);
updateEmployee(employee);
}
}
@Override
protected void stop() {
String comment =
String.format(I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_ENDING_0) + '\n', total);
comment +=
String.format(
I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_ENDING_1) + '\n', batch.getDone());
if (confAnomaly > 0) {
comment +=
String.format(
I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_ENDING_2) + '\n', confAnomaly);
}
if (noValueAnomaly > 0) {
comment +=
String.format(
I18n.get(IExceptionMessage.BATCH_LEAVE_MANAGEMENT_ENDING_3) + '\n', noValueAnomaly);
}
addComment(comment);
super.stop();
}
}

View File

@ -0,0 +1,50 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.base.service.administration.AbstractBatch;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.service.leave.management.LeaveManagementService;
import com.axelor.apps.hr.service.publicHoliday.PublicHolidayHrService;
import com.google.inject.Inject;
public abstract class BatchStrategy extends AbstractBatch {
protected LeaveManagementService leaveManagementService;
@Inject protected EmployeeRepository employeeRepository;
@Inject protected PublicHolidayHrService publicHolidayService;
public BatchStrategy(LeaveManagementService leaveManagementService) {
super();
this.leaveManagementService = leaveManagementService;
}
public BatchStrategy() {
super();
}
protected void updateEmployee(Employee employee) {
employee.addBatchSetItem(batchRepo.find(batch.getId()));
incrementDone();
}
}

View File

@ -0,0 +1,130 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.leave.management.LeaveManagementService;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.db.repo.EmailAccountRepository;
import com.axelor.apps.message.db.repo.MessageRepository;
import com.axelor.apps.message.service.MessageService;
import com.axelor.auth.AuthUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import java.io.IOException;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.List;
import javax.mail.MessagingException;
public class BatchTimesheetReminder extends BatchStrategy {
protected TimesheetRepository timesheetRepo;
protected MessageService messageService;
@Inject
public BatchTimesheetReminder(
LeaveManagementService leaveManagementService,
TimesheetRepository timesheetRepo,
MessageService messageService) {
super(leaveManagementService);
this.timesheetRepo = timesheetRepo;
this.messageService = messageService;
}
@Override
protected void process() {
for (Employee employee : getEmployeesWithoutRecentTimesheet()) {
try {
sendReminder(employee);
} catch (Exception e) {
TraceBackService.trace(e, Employee.class.getSimpleName(), batch.getId());
incrementAnomaly();
} finally {
incrementDone();
}
}
}
@Override
protected void stop() {
String comment =
String.format(I18n.get(IExceptionMessage.BATCH_TIMESHEET_REMINDER_DONE), batch.getDone())
+ "<br/>"
+ String.format(
I18n.get(IExceptionMessage.BATCH_TIMESHEET_REMINDER_ANOMALY), batch.getAnomaly());
addComment(comment);
super.stop();
}
private List<Employee> getEmployeesWithoutRecentTimesheet() {
LocalDate now = LocalDate.now();
long daysBeforeReminder = batch.getHrBatch().getDaysBeforeReminder().longValue();
List<Employee> employees =
employeeRepository
.all()
.filter(
"self.timesheetReminder = 't' AND self.mainEmploymentContract.payCompany = :companyId")
.bind("companyId", batch.getHrBatch().getCompany().getId())
.fetch();
employees.removeIf(employee -> hasRecentTimesheet(now, daysBeforeReminder, employee));
return employees;
}
private boolean hasRecentTimesheet(LocalDate now, long daysBeforeReminder, Employee employee) {
Timesheet timesheet =
timesheetRepo
.all()
.filter(
"self.user.id = :userId AND self.statusSelect IN (:confirmed, :validated) AND self.company = :companyId")
.bind("userId", employee.getUser().getId())
.bind("confirmed", TimesheetRepository.STATUS_CONFIRMED)
.bind("validated", TimesheetRepository.STATUS_VALIDATED)
.bind("companyId", batch.getHrBatch().getCompany().getId())
.order("-toDate")
.fetchOne();
return timesheet != null && timesheet.getToDate().plusDays(daysBeforeReminder).isAfter(now);
}
private void sendReminder(Employee employee)
throws AxelorException, MessagingException, IOException {
Message message = new Message();
message.setMediaTypeSelect(MessageRepository.MEDIA_TYPE_EMAIL);
message.setReplyToEmailAddressSet(new HashSet<>());
message.setCcEmailAddressSet(new HashSet<>());
message.setBccEmailAddressSet(new HashSet<>());
message.addToEmailAddressSetItem(employee.getContactPartner().getEmailAddress());
message.setSenderUser(AuthUtils.getUser());
message.setSubject(batch.getHrBatch().getTemplate().getSubject());
message.setContent(batch.getHrBatch().getTemplate().getContent());
message.setMailAccount(
Beans.get(EmailAccountRepository.class).all().filter("self.isDefault = true").fetchOne());
messageService.sendByEmail(message);
}
}

View File

@ -0,0 +1,218 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.repo.CompanyRepository;
import com.axelor.apps.base.db.repo.MailBatchRepository;
import com.axelor.apps.base.service.administration.AbstractBatch;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.db.Template;
import com.axelor.apps.message.db.repo.EmailAccountRepository;
import com.axelor.apps.message.db.repo.MessageRepository;
import com.axelor.apps.message.service.MessageService;
import com.axelor.apps.message.service.TemplateMessageService;
import com.axelor.auth.AuthUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.ExceptionOriginRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import javax.mail.MessagingException;
public class BatchTimesheetValidationReminder extends AbstractBatch {
protected TemplateMessageService templateMessageService;
protected MessageService messageService;
protected MessageRepository messageRepo;
@Inject
public BatchTimesheetValidationReminder(
TemplateMessageService templateMessageService,
MessageService messageService,
MessageRepository messageRepo) {
this.templateMessageService = templateMessageService;
this.messageService = messageService;
this.messageRepo = messageRepo;
}
@Override
protected void process() {
Template template = batch.getMailBatch().getTemplate();
switch (batch.getMailBatch().getCode()) {
case MailBatchRepository.CODE_BATCH_EMAIL_TIME_SHEET:
if (template != null) {
generateEmailTemplate();
} else {
generateEmail();
}
break;
case MailBatchRepository.CODE_BATCH_EMAIL_ALL_TIME_SHEET:
if (template != null) {
generateAllEmailTemplate();
} else {
generateAllEmail();
}
break;
default:
return;
}
}
public void generateEmailTemplate() {
Company company = batch.getMailBatch().getCompany();
Template template = batch.getMailBatch().getTemplate();
List<Timesheet> timesheetList = null;
if (Beans.get(CompanyRepository.class).all().count() > 1) {
timesheetList =
Beans.get(TimesheetRepository.class)
.all()
.filter(
"self.company.id = ?1 AND self.statusSelect = 1 AND self.user.employee.timesheetReminder = true",
company.getId())
.fetch();
} else {
timesheetList =
Beans.get(TimesheetRepository.class)
.all()
.filter("self.statusSelect = 1 AND self.user.employee.timesheetReminder = true")
.fetch();
}
String model = template.getMetaModel().getFullName();
String tag = template.getMetaModel().getName();
for (Timesheet timesheet : timesheetList) {
try {
Message message =
templateMessageService.generateMessage(
timesheet.getUser().getEmployee().getId(), model, tag, template);
messageService.sendByEmail(message);
incrementDone();
} catch (Exception e) {
incrementAnomaly();
TraceBackService.trace(new Exception(e), ExceptionOriginRepository.REMINDER, batch.getId());
}
}
}
public void generateEmail() {
List<Timesheet> timesheetList =
Beans.get(CompanyRepository.class).all().count() > 1
? Beans.get(TimesheetRepository.class)
.all()
.filter(
"self.company.id = ?1 AND self.statusSelect = 1 AND self.user.employee.timesheetReminder = true",
batch.getMailBatch().getCompany().getId())
.fetch()
: Beans.get(TimesheetRepository.class)
.all()
.filter("self.statusSelect = 1 AND self.user.employee.timesheetReminder = true")
.fetch();
for (Timesheet timesheet : timesheetList) {
try {
generateAndSendMessage(timesheet.getUser().getEmployee());
incrementDone();
} catch (Exception e) {
incrementAnomaly();
TraceBackService.trace(
new Exception(e), ExceptionOriginRepository.INVOICE_ORIGIN, batch.getId());
}
}
}
public void generateAllEmailTemplate() {
Template template = batch.getMailBatch().getTemplate();
String model = template.getMetaModel().getFullName();
String tag = template.getMetaModel().getName();
List<Employee> employeeList =
Beans.get(EmployeeRepository.class).all().filter("self.timesheetReminder = true").fetch();
for (Employee employee : employeeList) {
try {
Message message =
templateMessageService.generateMessage(employee.getId(), model, tag, template);
messageService.sendByEmail(message);
incrementDone();
} catch (Exception e) {
incrementAnomaly();
TraceBackService.trace(new Exception(e), ExceptionOriginRepository.REMINDER, batch.getId());
}
}
}
public void generateAllEmail() {
List<Employee> employeeList =
Beans.get(EmployeeRepository.class).all().filter("self.timesheetReminder = true").fetch();
for (Employee employee : employeeList) {
try {
generateAndSendMessage(employee);
incrementDone();
} catch (Exception e) {
incrementAnomaly();
TraceBackService.trace(
new Exception(e), ExceptionOriginRepository.INVOICE_ORIGIN, batch.getId());
}
}
}
@Transactional(rollbackOn = {MessagingException.class, IOException.class, Exception.class})
protected Message generateAndSendMessage(Employee employee)
throws MessagingException, IOException, AxelorException {
Message message = new Message();
message.setMediaTypeSelect(MessageRepository.MEDIA_TYPE_EMAIL);
message.setReplyToEmailAddressSet(new HashSet<>());
message.setCcEmailAddressSet(new HashSet<>());
message.setBccEmailAddressSet(new HashSet<>());
message.addToEmailAddressSetItem(employee.getContactPartner().getEmailAddress());
message.setSenderUser(AuthUtils.getUser());
message.setSubject(batch.getMailBatch().getSubject());
message.setContent(batch.getMailBatch().getContent());
message.setMailAccount(
Beans.get(EmailAccountRepository.class).all().filter("self.isDefault = true").fetchOne());
message = messageRepo.save(message);
return messageService.sendByEmail(message);
}
@Override
protected void stop() {
String comment = String.format("\t* %s Email(s) sent %n", batch.getDone());
comment +=
String.format(
"\t" + I18n.get(com.axelor.apps.base.exceptions.IExceptionMessage.ALARM_ENGINE_BATCH_4),
batch.getAnomaly());
super.stop();
addComment(comment);
}
}

View File

@ -0,0 +1,111 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.base.db.Batch;
import com.axelor.apps.base.exceptions.IExceptionMessage;
import com.axelor.apps.base.service.administration.AbstractBatchService;
import com.axelor.apps.hr.db.HrBatch;
import com.axelor.apps.hr.db.repo.HrBatchHRRepository;
import com.axelor.apps.hr.db.repo.HrBatchRepository;
import com.axelor.db.Model;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
public class HrBatchService extends AbstractBatchService {
@Override
protected Class<? extends Model> getModelClass() {
return HrBatch.class;
}
@Override
public Batch run(Model batchModel) throws AxelorException {
HrBatch hrBatch = (HrBatch) batchModel;
Batch batch = null;
switch (hrBatch.getActionSelect()) {
case HrBatchRepository.ACTION_LEAVE_MANAGEMENT:
batch = leaveManagement(hrBatch);
break;
case HrBatchRepository.ACTION_SENIORITY_LEAVE_MANAGEMENT:
batch = seniorityLeaveManagement(hrBatch);
break;
case HrBatchRepository.ACTION_PAYROLL_PREPARATION_GENERATION:
batch = payrollPreparationGeneration(hrBatch);
break;
case HrBatchRepository.ACTION_PAYROLL_PREPARATION_EXPORT:
batch = payrollPreparationExport(hrBatch);
break;
case HrBatchRepository.ACTION_LEAVE_MANAGEMENT_RESET:
batch = leaveManagementReset(hrBatch);
break;
case HrBatchRepository.ACTION_EMPLOYMENT_CONTRACT_EXPORT:
batch = employmentContractExport(hrBatch);
break;
case HrBatchHRRepository.ACTION_TIMESHEET_REMINDER:
batch = runTimesheetReminderBatch(hrBatch);
break;
default:
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY,
I18n.get(IExceptionMessage.BASE_BATCH_1),
hrBatch.getActionSelect(),
hrBatch.getCode());
}
return batch;
}
public Batch leaveManagement(HrBatch hrBatch) {
return Beans.get(BatchLeaveManagement.class).run(hrBatch);
}
public Batch seniorityLeaveManagement(HrBatch hrBatch) {
return Beans.get(BatchSeniorityLeaveManagement.class).run(hrBatch);
}
public Batch payrollPreparationGeneration(HrBatch hrBatch) {
return Beans.get(BatchPayrollPreparationGeneration.class).run(hrBatch);
}
public Batch payrollPreparationExport(HrBatch hrBatch) {
return Beans.get(BatchPayrollPreparationExport.class).run(hrBatch);
}
public Batch leaveManagementReset(HrBatch hrBatch) {
return Beans.get(BatchLeaveManagementReset.class).run(hrBatch);
}
public Batch employmentContractExport(HrBatch hrBatch) {
return Beans.get(BatchEmploymentContractExport.class).run(hrBatch);
}
public Batch runTimesheetReminderBatch(HrBatch hrBatch) throws AxelorException {
if (hrBatch.getTemplate() == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_NO_VALUE,
com.axelor.apps.hr.exception.IExceptionMessage.BATCH_TIMESHEET_MISSING_TEMPLATE);
}
return Beans.get(BatchTimesheetReminder.class).run(hrBatch);
}
}

View File

@ -0,0 +1,46 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.batch;
import com.axelor.apps.base.db.Batch;
import com.axelor.apps.base.db.MailBatch;
import com.axelor.apps.base.db.repo.MailBatchRepository;
import com.axelor.apps.base.service.batch.MailBatchService;
import com.axelor.db.Model;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
public class MailBatchServiceHR extends MailBatchService {
@Override
public Batch run(Model batchModel) throws AxelorException {
MailBatch mailBatch = (MailBatch) batchModel;
switch (mailBatch.getActionSelect()) {
case MailBatchRepository.ACTION_TIMESHEET_VALIDATION_REMINDER:
return runTimesheetValidationReminderBatch(mailBatch);
default:
return super.run(batchModel);
}
}
public Batch runTimesheetValidationReminderBatch(MailBatch mailBatch) {
return Beans.get(BatchTimesheetValidationReminder.class).run(mailBatch);
}
}

View File

@ -0,0 +1,63 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.config;
import com.axelor.apps.account.db.Account;
import com.axelor.apps.account.db.AccountConfig;
import com.axelor.apps.account.db.Journal;
import com.axelor.apps.account.service.config.AccountConfigService;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
public class AccountConfigHRService extends AccountConfigService {
public Journal getExpenseJournal(AccountConfig accountConfig) throws AxelorException {
if (accountConfig.getExpenseJournal() == null) {
throw new AxelorException(
accountConfig,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EXPENSE_JOURNAL),
accountConfig.getCompany().getName());
}
return accountConfig.getExpenseJournal();
}
public Account getExpenseEmployeeAccount(AccountConfig accountConfig) throws AxelorException {
if (accountConfig.getEmployeeAccount() == null) {
throw new AxelorException(
accountConfig,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EXPENSE_ACCOUNT),
accountConfig.getCompany().getName());
}
return accountConfig.getEmployeeAccount();
}
public Account getExpenseTaxAccount(AccountConfig accountConfig) throws AxelorException {
if (accountConfig.getExpenseTaxAccount() == null) {
throw new AxelorException(
accountConfig,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EXPENSE_ACCOUNT_TAX),
accountConfig.getCompany().getName());
}
return accountConfig.getExpenseTaxAccount();
}
}

View File

@ -0,0 +1,260 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.config;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Sequence;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.LeaveReason;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.message.db.Template;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
public class HRConfigService {
public HRConfig getHRConfig(Company company) throws AxelorException {
HRConfig hrConfig = company.getHrConfig();
if (hrConfig == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG),
company.getName());
}
return hrConfig;
}
public Sequence getExpenseSequence(HRConfig hrConfig) throws AxelorException {
Sequence sequence = hrConfig.getExpenseSequence();
if (sequence == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_NO_EXPENSE_SEQUENCE),
hrConfig.getCompany().getName());
}
return sequence;
}
public LeaveReason getLeaveReason(HRConfig hrConfig) throws AxelorException {
LeaveReason leaveReason = hrConfig.getToJustifyLeaveReason();
if (leaveReason == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_LEAVE_REASON),
hrConfig.getCompany().getName());
}
return leaveReason;
}
public Product getKilometricExpenseProduct(HRConfig hrConfig) throws AxelorException {
Product kilometricExpenseProduct = hrConfig.getKilometricExpenseProduct();
if (kilometricExpenseProduct == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_EXPENSE_TYPE),
hrConfig.getCompany().getName());
}
return kilometricExpenseProduct;
}
// EXPENSE
public Template getSentExpenseTemplate(HRConfig hrConfig) throws AxelorException {
Template sentExpenseTemplate = hrConfig.getSentExpenseTemplate();
if (sentExpenseTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_SENT_EXPENSE_TEMPLATE),
hrConfig.getCompany().getName());
}
return sentExpenseTemplate;
}
public Template getValidatedExpenseTemplate(HRConfig hrConfig) throws AxelorException {
Template validatedExpenseTemplate = hrConfig.getValidatedExpenseTemplate();
if (validatedExpenseTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_VALIDATED_EXPENSE_TEMPLATE),
hrConfig.getCompany().getName());
}
return validatedExpenseTemplate;
}
public Template getRefusedExpenseTemplate(HRConfig hrConfig) throws AxelorException {
Template refusedExpenseTemplate = hrConfig.getRefusedExpenseTemplate();
if (refusedExpenseTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_REFUSED_EXPENSE_TEMPLATE),
hrConfig.getCompany().getName());
}
return refusedExpenseTemplate;
}
public Template getCanceledExpenseTemplate(HRConfig hrConfig) throws AxelorException {
Template refusedExpenseTemplate = hrConfig.getCanceledExpenseTemplate();
if (refusedExpenseTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_CANCELED_EXPENSE_TEMPLATE),
hrConfig.getCompany().getName());
}
return refusedExpenseTemplate;
}
// TIMESHEET
public Template getSentTimesheetTemplate(HRConfig hrConfig) throws AxelorException {
Template sentTimesheetTemplate = hrConfig.getSentTimesheetTemplate();
if (sentTimesheetTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_SENT_TIMESHEET_TEMPLATE),
hrConfig.getCompany().getName());
}
return sentTimesheetTemplate;
}
public Template getValidatedTimesheetTemplate(HRConfig hrConfig) throws AxelorException {
Template validatedTimesheetTemplate = hrConfig.getValidatedTimesheetTemplate();
if (validatedTimesheetTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_VALIDATED_TIMESHEET_TEMPLATE),
hrConfig.getCompany().getName());
}
return validatedTimesheetTemplate;
}
public Template getRefusedTimesheetTemplate(HRConfig hrConfig) throws AxelorException {
Template refusedTimesheetTemplate = hrConfig.getRefusedTimesheetTemplate();
if (refusedTimesheetTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_REFUSED_TIMESHEET_TEMPLATE),
hrConfig.getCompany().getName());
}
return refusedTimesheetTemplate;
}
public Template getCanceledTimesheetTemplate(HRConfig hrConfig) throws AxelorException {
Template refusedTimesheetTemplate = hrConfig.getCanceledTimesheetTemplate();
if (refusedTimesheetTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_CANCELED_TIMESHEET_TEMPLATE),
hrConfig.getCompany().getName());
}
return refusedTimesheetTemplate;
}
// LEAVE REQUEST
public Template getSentLeaveTemplate(HRConfig hrConfig) throws AxelorException {
Template sentLeaveTemplate = hrConfig.getSentLeaveTemplate();
if (sentLeaveTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_SENT_LEAVE_TEMPLATE),
hrConfig.getCompany().getName());
}
return sentLeaveTemplate;
}
public Template getValidatedLeaveTemplate(HRConfig hrConfig) throws AxelorException {
Template validatedLeaveTemplate = hrConfig.getValidatedLeaveTemplate();
if (validatedLeaveTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_VALIDATED_LEAVE_TEMPLATE),
hrConfig.getCompany().getName());
}
return validatedLeaveTemplate;
}
public Template getRefusedLeaveTemplate(HRConfig hrConfig) throws AxelorException {
Template refusedLeaveTemplate = hrConfig.getRefusedLeaveTemplate();
if (refusedLeaveTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_REFUSED_LEAVE_TEMPLATE),
hrConfig.getCompany().getName());
}
return refusedLeaveTemplate;
}
public Template getCanceledLeaveTemplate(HRConfig hrConfig) throws AxelorException {
Template refusedLeaveTemplate = hrConfig.getCanceledLeaveTemplate();
if (refusedLeaveTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_CANCELED_LEAVE_TEMPLATE),
hrConfig.getCompany().getName());
}
return refusedLeaveTemplate;
}
// EXTRA HOURS
public Template getSentExtraHoursTemplate(HRConfig hrConfig) throws AxelorException {
Template sentExtraHoursTemplate = hrConfig.getSentExtraHoursTemplate();
if (sentExtraHoursTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_SENT_EXTRA_HOURS_TEMPLATE),
hrConfig.getCompany().getName());
}
return sentExtraHoursTemplate;
}
public Template getValidatedExtraHoursTemplate(HRConfig hrConfig) throws AxelorException {
Template validatedExtraHoursTemplate = hrConfig.getValidatedExtraHoursTemplate();
if (validatedExtraHoursTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_VALIDATED_EXTRA_HOURS_TEMPLATE),
hrConfig.getCompany().getName());
}
return validatedExtraHoursTemplate;
}
public Template getRefusedExtraHoursTemplate(HRConfig hrConfig) throws AxelorException {
Template refusedExtraHoursTemplate = hrConfig.getRefusedExtraHoursTemplate();
if (refusedExtraHoursTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_REFUSED_EXTRA_HOURS_TEMPLATE),
hrConfig.getCompany().getName());
}
return refusedExtraHoursTemplate;
}
public Template getCanceledExtraHoursTemplate(HRConfig hrConfig) throws AxelorException {
Template refusedExtraHoursTemplate = hrConfig.getCanceledExtraHoursTemplate();
if (refusedExtraHoursTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.HR_CONFIG_CANCELED_EXTRA_HOURS_TEMPLATE),
hrConfig.getCompany().getName());
}
return refusedExtraHoursTemplate;
}
}

View File

@ -0,0 +1,57 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.employee;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.apps.hr.db.DPAE;
import com.axelor.apps.hr.db.Employee;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Map;
import java.util.List;
public interface EmployeeService extends UserService {
public int getLengthOfService(Employee employee, LocalDate refDate) throws AxelorException;
public int getAge(Employee employee, LocalDate refDate) throws AxelorException;
public BigDecimal getDaysWorksInPeriod(Employee employee, LocalDate fromDate, LocalDate toDate)
throws AxelorException;
public BigDecimal getDaysWorkedInPeriod(Employee employee, LocalDate fromDate, LocalDate toDate)
throws AxelorException;
public Map<String, String> getSocialNetworkUrl(String name, String firstName);
/** Generates a new {@link DPAE} for given {@link Employee} and returns its id. */
@Transactional(rollbackOn = {Exception.class})
Long generateNewDPAE(Employee employee) throws AxelorException;
@Transactional
public void setEmployeeEnrolled(Employee employee);
@Transactional(rollbackOn = {Exception.class})
public void updateEmployeeConfig(Employee employee, Integer configSelect, Boolean status) throws AxelorException ;
@Transactional(rollbackOn = {Exception.class})
public void massUpdateEmployeeConfig(List<Long> employeesIds, Integer configSelect, Boolean status) throws AxelorException;
}

View File

@ -0,0 +1,294 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.employee;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.EventsPlanning;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.WeeklyPlanning;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.base.service.user.UserServiceImpl;
import com.axelor.apps.base.service.weeklyplanning.WeeklyPlanningService;
import com.axelor.apps.hr.db.DPAE;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.LeaveRequest;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.LeaveRequestRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.leave.LeaveService;
import com.axelor.apps.hr.service.publicHoliday.PublicHolidayHrService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.Period;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EmployeeServiceImpl extends UserServiceImpl implements EmployeeService {
@Inject protected WeeklyPlanningService weeklyPlanningService;
public int getLengthOfService(Employee employee, LocalDate refDate) throws AxelorException {
try {
Period period =
Period.between(
employee.getSeniorityDate(),
refDate == null ? Beans.get(AppBaseService.class).getTodayDate() : refDate);
return period.getYears();
} catch (IllegalArgumentException e) {
throw new AxelorException(
e.getCause(),
employee,
TraceBackRepository.CATEGORY_NO_VALUE,
I18n.get(IExceptionMessage.EMPLOYEE_NO_SENIORITY_DATE),
employee.getName());
}
}
public int getAge(Employee employee, LocalDate refDate) throws AxelorException {
try {
Period period =
Period.between(
employee.getBirthDate(),
refDate == null ? Beans.get(AppBaseService.class).getTodayDate() : refDate);
return period.getYears();
} catch (IllegalArgumentException e) {
throw new AxelorException(
e.getCause(),
employee,
TraceBackRepository.CATEGORY_NO_VALUE,
I18n.get(IExceptionMessage.EMPLOYEE_NO_BIRTH_DATE),
employee.getName());
}
}
@Override
public BigDecimal getDaysWorksInPeriod(Employee employee, LocalDate fromDate, LocalDate toDate)
throws AxelorException {
Company company = employee.getMainEmploymentContract().getPayCompany();
BigDecimal duration = BigDecimal.ZERO;
WeeklyPlanning weeklyPlanning = employee.getWeeklyPlanning();
if (weeklyPlanning == null) {
HRConfig conf = company.getHrConfig();
if (conf != null) {
weeklyPlanning = conf.getWeeklyPlanning();
}
}
if (weeklyPlanning == null) {
throw new AxelorException(
employee,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EMPLOYEE_PLANNING),
employee.getName());
}
EventsPlanning publicHolidayPlanning = employee.getPublicHolidayEventsPlanning();
if (publicHolidayPlanning == null) {
HRConfig conf = company.getHrConfig();
if (conf != null) {
publicHolidayPlanning = conf.getPublicHolidayEventsPlanning();
}
}
if (publicHolidayPlanning == null) {
throw new AxelorException(
employee,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EMPLOYEE_PUBLIC_HOLIDAY),
employee.getName());
}
LocalDate date = fromDate;
while (!date.isAfter(toDate)) {
duration =
duration.add(
BigDecimal.valueOf(
weeklyPlanningService.getWorkingDayValueInDays(weeklyPlanning, date)));
date = date.plusDays(1);
}
duration =
duration.subtract(
Beans.get(PublicHolidayHrService.class)
.computePublicHolidayDays(fromDate, toDate, weeklyPlanning, publicHolidayPlanning));
return duration;
}
@Override
public BigDecimal getDaysWorkedInPeriod(Employee employee, LocalDate fromDate, LocalDate toDate)
throws AxelorException {
BigDecimal daysWorks = getDaysWorksInPeriod(employee, fromDate, toDate);
BigDecimal daysLeave = BigDecimal.ZERO;
List<LeaveRequest> leaveRequestList =
Beans.get(LeaveRequestRepository.class)
.all()
.filter(
"self.user = ?1 AND self.duration >= 1 AND self.statusSelect = ?2 AND (self.fromDateT BETWEEN ?3 AND ?4 OR self.toDateT BETWEEN ?3 AND ?4)",
employee.getUser(),
LeaveRequestRepository.STATUS_VALIDATED,
fromDate,
toDate)
.fetch();
for (LeaveRequest leaveRequest : leaveRequestList) {
daysLeave =
daysLeave.add(
Beans.get(LeaveService.class).computeDuration(leaveRequest, fromDate, toDate));
}
return daysWorks.subtract(daysLeave);
}
public Map<String, String> getSocialNetworkUrl(String name, String firstName) {
Map<String, String> urlMap = new HashMap<>();
name =
firstName != null && name != null
? firstName + "+" + name
: name == null ? firstName : name;
name = name == null ? "" : name;
urlMap.put(
"facebook",
"<a class='fa fa-facebook' href='https://www.facebook.com/search/more/?q="
+ name
+ "&init=public"
+ "' target='_blank'/>");
urlMap.put(
"twitter",
"<a class='fa fa-twitter' href='https://twitter.com/search?q="
+ name
+ "' target='_blank' />");
urlMap.put(
"linkedin",
"<a class='fa fa-linkedin' href='http://www.linkedin.com/pub/dir/"
+ name.replace("+", "/")
+ "' target='_blank' />");
urlMap.put(
"youtube",
"<a class='fa fa-youtube' href='https://www.youtube.com/results?search_query="
+ name
+ "' target='_blank' />");
return urlMap;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public Long generateNewDPAE(Employee employee) throws AxelorException {
EmploymentContract mainEmploymentContract = employee.getMainEmploymentContract();
if (mainEmploymentContract == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get(IExceptionMessage.EMPLOYEE_CONTRACT_OF_EMPLOYMENT),
employee.getName());
}
Company payCompany = mainEmploymentContract.getPayCompany();
Partner employer = payCompany.getPartner();
DPAE newDPAE = new DPAE();
// Employer
newDPAE.setRegistrationCode(employer.getRegistrationCode());
newDPAE.setMainActivityCode(employer.getMainActivityCode());
newDPAE.setCompany(payCompany);
newDPAE.setCompanyAddress(employer.getMainAddress());
newDPAE.setCompanyFixedPhone(employer.getFixedPhone());
if (payCompany.getHrConfig() != null) {
newDPAE.setHealthService(payCompany.getHrConfig().getHealthService());
newDPAE.setHealthServiceAddress(payCompany.getHrConfig().getHealthServiceAddress());
}
// Employee
newDPAE.setLastName(employee.getContactPartner().getName());
newDPAE.setFirstName(employee.getContactPartner().getFirstName());
newDPAE.setSocialSecurityNumber(employee.getSocialSecurityNumber());
newDPAE.setSexSelect(employee.getSexSelect());
newDPAE.setDateOfBirth(employee.getBirthDate());
newDPAE.setDepartmentOfBirth(employee.getDepartmentOfBirth());
newDPAE.setCityOfBirth(employee.getCityOfBirth());
newDPAE.setCountryOfBirth(employee.getCountryOfBirth());
// Contract
newDPAE.setDateOfHire(mainEmploymentContract.getStartDate());
newDPAE.setTimeOfHire(mainEmploymentContract.getStartTime());
newDPAE.setTrialPeriodDuration(mainEmploymentContract.getTrialPeriodDuration());
newDPAE.setContractType(mainEmploymentContract.getContractType());
newDPAE.setEndDateOfContract(mainEmploymentContract.getEndDate());
employee.addDpaeListItem(newDPAE);
Beans.get(EmployeeRepository.class).save(employee);
return newDPAE.getId();
}
@Override
@Transactional
public void setEmployeeEnrolled(Employee employee) {
employee.setIsEnrolled(true);
Beans.get(EmployeeRepository.class).save(employee);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void updateEmployeeConfig(Employee employee, Integer configSelect, Boolean status)
throws AxelorException {
switch (configSelect) {
case 1: // ITP
employee.setHasItp(status);
break;
case 2: // Nuissance
employee.setHasNuissance(status);
break;
case 3: // Transfaire
employee.setIsTransfaire(status);
break;
default:
return; // Invalid configSelect, stop processing
}
Beans.get(EmployeeRepository.class).save(employee);
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void massUpdateEmployeeConfig(
List<Long> employeesIds, Integer configSelect, Boolean status) throws AxelorException {
List<Employee> employees =
Beans.get(EmployeeRepository.class).all().filter("self.id in (?1)", employeesIds).fetch();
if (employeesIds != null || !employeesIds.isEmpty()) {
for (Employee employee : employees) {
this.updateEmployeeConfig(employee, configSelect, status);
}
}
}
}

View File

@ -0,0 +1,136 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.expense;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.InvoiceLine;
import com.axelor.apps.account.db.Move;
import com.axelor.apps.base.db.BankDetails;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.ExpenseLine;
import com.axelor.apps.hr.db.KilometricAllowParam;
import com.axelor.apps.message.db.Message;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import javax.mail.MessagingException;
public interface ExpenseService {
public ExpenseLine getAndComputeAnalyticDistribution(ExpenseLine expenseLine, Expense expense)
throws AxelorException;
public ExpenseLine createAnalyticDistributionWithTemplate(ExpenseLine expenseLine)
throws AxelorException;
public ExpenseLine computeAnalyticDistribution(ExpenseLine expenseLine) throws AxelorException;
public Expense compute(Expense expense);
@Transactional(rollbackOn = {Exception.class})
public void confirm(Expense expense) throws AxelorException;
public Message sendConfirmationEmail(Expense expense)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void validate(Expense expense) throws AxelorException;
public Message sendValidationEmail(Expense expense)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void refuse(Expense expense) throws AxelorException;
public Message sendRefusalEmail(Expense expense)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public Move ventilate(Expense expense) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void cancel(Expense expense) throws AxelorException;
public Message sendCancellationEmail(Expense expense)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void addPayment(Expense expense, BankDetails bankDetails) throws AxelorException;
public void addPayment(Expense expense) throws AxelorException;
/**
* Cancel the payment in the expense in argument. Revert the payment status and clear all payment
* fields.
*
* @param expense
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
public void cancelPayment(Expense expense) throws AxelorException;
public List<InvoiceLine> createInvoiceLines(
Invoice invoice, List<ExpenseLine> expenseLineList, int priority) throws AxelorException;
public List<InvoiceLine> createInvoiceLine(Invoice invoice, ExpenseLine expenseLine, int priority)
throws AxelorException;
/**
* Get the expense from user, if no expense is found create one.
*
* @param user
* @return
*/
public Expense getOrCreateExpense(User user);
public BigDecimal computePersonalExpenseAmount(Expense expense);
public BigDecimal computeAdvanceAmount(Expense expense);
public Product getKilometricExpenseProduct(Expense expense) throws AxelorException;
public void setDraftSequence(Expense expense) throws AxelorException;
public List<KilometricAllowParam> getListOfKilometricAllowParamVehicleFilter(
ExpenseLine expenseLine) throws AxelorException;
public List<ExpenseLine> getExpenseLineList(Expense expense);
/**
* fill {@link ExpenseLine#expense} in {@link Expense#generalExpenseLineList} and {@link
* Expense#kilometricExpenseLineList}
*
* @param expense
*/
void completeExpenseLines(Expense expense);
public List<KilometricAllowParam> getListOfKilometricAllowParamVehicleFilter(
ExpenseLine expenseLine, Expense expense) throws AxelorException;
public Move createMoveForExpensePayment(Expense expense) throws AxelorException;
public Expense updateMoveDateAndPeriod(Expense expense);
}

View File

@ -0,0 +1,62 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.extra.hours;
import com.axelor.apps.hr.db.ExtraHours;
import com.axelor.apps.message.db.Message;
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 {
@Transactional(rollbackOn = {Exception.class})
public void cancel(ExtraHours extraHours) throws AxelorException;
public Message sendCancellationEmail(ExtraHours extraHours)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void confirm(ExtraHours extraHours) throws AxelorException;
public Message sendConfirmationEmail(ExtraHours extraHours)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void validate(ExtraHours extraHours) throws AxelorException;
public Message sendValidationEmail(ExtraHours extraHours)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@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;
public void compute(ExtraHours extraHours);
}

View File

@ -0,0 +1,375 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.extra.hours;
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.DailyReport;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.ExtraHours;
import com.axelor.apps.hr.db.ExtraHoursLine;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.ExtraHoursRepository;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.service.TemplateMessageService;
import com.axelor.auth.AuthUtils;
import com.axelor.exception.AxelorException;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.math.BigDecimal;
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.util.List;
import javax.mail.MessagingException;
import wslite.json.JSONException;
import wslite.json.JSONObject;
public class ExtraHoursServiceImpl implements ExtraHoursService {
protected ExtraHoursRepository extraHoursRepo;
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;
}
@Transactional(rollbackOn = {Exception.class})
public void cancel(ExtraHours extraHours) throws AxelorException {
extraHours.setStatusSelect(ExtraHoursRepository.STATUS_CANCELED);
extraHoursRepo.save(extraHours);
}
public Message sendCancellationEmail(ExtraHours extraHours)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException {
HRConfig hrConfig = hrConfigService.getHRConfig(extraHours.getCompany());
if (hrConfig.getTimesheetMailNotification()) {
return templateMessageService.generateAndSendMessage(
extraHours, hrConfigService.getCanceledExtraHoursTemplate(hrConfig));
}
return null;
}
@Transactional(rollbackOn = {Exception.class})
public void confirm(ExtraHours extraHours) throws AxelorException {
extraHours.setStatusSelect(ExtraHoursRepository.STATUS_CONFIRMED);
extraHours.setSentDate(appBaseService.getTodayDate());
extraHoursRepo.save(extraHours);
}
public Message sendConfirmationEmail(ExtraHours extraHours)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException {
HRConfig hrConfig = hrConfigService.getHRConfig(extraHours.getCompany());
if (hrConfig.getExtraHoursMailNotification()) {
return templateMessageService.generateAndSendMessage(
extraHours, hrConfigService.getSentExtraHoursTemplate(hrConfig));
}
return null;
}
@Transactional(rollbackOn = {Exception.class})
public void validate(ExtraHours extraHours) throws AxelorException {
extraHours.setStatusSelect(ExtraHoursRepository.STATUS_VALIDATED);
extraHours.setValidatedBy(AuthUtils.getUser());
extraHours.setValidationDate(appBaseService.getTodayDate());
extraHoursRepo.save(extraHours);
}
public Message sendValidationEmail(ExtraHours extraHours)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException {
HRConfig hrConfig = hrConfigService.getHRConfig(extraHours.getCompany());
if (hrConfig.getExtraHoursMailNotification()) {
return templateMessageService.generateAndSendMessage(
extraHours, hrConfigService.getValidatedExtraHoursTemplate(hrConfig));
}
return null;
}
@Transactional(rollbackOn = {Exception.class})
public void refuse(ExtraHours extraHours) throws AxelorException {
extraHours.setStatusSelect(ExtraHoursRepository.STATUS_REFUSED);
extraHours.setRefusedBy(AuthUtils.getUser());
extraHours.setRefusalDate(appBaseService.getTodayDate());
extraHoursRepo.save(extraHours);
}
public Message sendRefusalEmail(ExtraHours extraHours)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException {
HRConfig hrConfig = hrConfigService.getHRConfig(extraHours.getCompany());
if (hrConfig.getExtraHoursMailNotification()) {
return templateMessageService.generateAndSendMessage(
extraHours, hrConfigService.getRefusedExtraHoursTemplate(hrConfig));
}
return null;
}
@Override
public void compute(ExtraHours extraHours) {
BigDecimal totalQty = BigDecimal.ZERO;
List<ExtraHoursLine> extraHoursLines = extraHours.getExtraHoursLineList();
for (ExtraHoursLine extraHoursLine : extraHoursLines) {
totalQty = totalQty.add(extraHoursLine.getQty());
}
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<ExtraHours> 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();
}
}
}

View File

@ -0,0 +1,129 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.leave;
import com.axelor.apps.base.db.WeeklyPlanning;
import com.axelor.apps.hr.db.Employee;
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.message.db.Message;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.io.IOException;
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)
throws AxelorException;
public BigDecimal computeDuration(
LeaveRequest leave, LocalDateTime from, LocalDateTime to, int startOn, int endOn)
throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void manageSentLeaves(LeaveRequest leave) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void manageValidateLeaves(LeaveRequest leave) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void manageRefuseLeaves(LeaveRequest leave) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void manageCancelLeaves(LeaveRequest leave) throws AxelorException;
public double computeStartDateWithSelect(
LocalDate date, int select, WeeklyPlanning weeklyPlanning);
public double computeEndDateWithSelect(LocalDate date, int select, WeeklyPlanning weeklyPlanning);
@Transactional(rollbackOn = {Exception.class})
public LeaveRequest createEvents(LeaveRequest leave) throws AxelorException;
public BigDecimal computeLeaveDaysByLeaveRequest(
LocalDate fromDate, LocalDate toDate, LeaveRequest leaveRequest, Employee employee)
throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void cancel(LeaveRequest leaveRequest) throws AxelorException;
public Message sendCancellationEmail(LeaveRequest leaveRequest)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void confirm(LeaveRequest leaveRequest) throws AxelorException;
public Message sendConfirmationEmail(LeaveRequest leaveRequest)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void validate(LeaveRequest leaveRequest) throws AxelorException;
public Message sendValidationEmail(LeaveRequest leaveRequest)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void refuse(LeaveRequest leaveRequest) throws AxelorException;
public Message sendRefusalEmail(LeaveRequest leaveRequest)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
public boolean willHaveEnoughDays(LeaveRequest leaveRequest);
@Transactional
public LeaveLine createLeaveReasonToJustify(Employee employee, LeaveReason leaveReasonHrConfig)
throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public LeaveLine addLeaveReasonOrCreateIt(Employee employee, LeaveReason leaveReason)
throws AxelorException;
/**
* Checks if the given day is a leave day.
*
* @param user
* @param date
* @return
*/
public boolean isLeaveDay(User user, LocalDate date);
/**
* Gets the leave for the given user for the given date.
*
* @param user
* @param date
* @return
*/
public LeaveRequest getLeave(User user, LocalDate date);
}

View File

@ -0,0 +1,99 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.leave.management;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.LeaveLine;
import com.axelor.apps.hr.db.LeaveManagement;
import com.axelor.auth.db.User;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
public class LeaveManagementService {
@Inject protected AppBaseService appBaseService;
public LeaveLine computeQuantityAvailable(LeaveLine leaveLine) {
List<LeaveManagement> leaveManagementList = leaveLine.getLeaveManagementList();
leaveLine.setTotalQuantity(BigDecimal.ZERO);
if (leaveManagementList != null && !leaveManagementList.isEmpty()) {
for (LeaveManagement leaveManagement : leaveManagementList) {
leaveLine.setTotalQuantity(leaveLine.getTotalQuantity().add(leaveManagement.getValue()));
}
leaveLine.setQuantity(leaveLine.getTotalQuantity().subtract(leaveLine.getDaysValidated()));
}
return leaveLine;
}
@Transactional
public LeaveManagement createLeaveManagement(
LeaveLine leaveLine,
User user,
String comments,
LocalDate date,
LocalDate fromDate,
LocalDate toDate,
BigDecimal value) {
LeaveManagement leaveManagement = new LeaveManagement();
leaveManagement.setLeaveLine(leaveLine);
leaveManagement.setUser(user);
leaveManagement.setComments(comments);
if (date == null) {
leaveManagement.setDate(appBaseService.getTodayDate());
} else {
leaveManagement.setDate(date);
}
leaveManagement.setFromDate(fromDate);
leaveManagement.setToDate(toDate);
leaveManagement.setValue(value.setScale(4, RoundingMode.HALF_EVEN));
return leaveManagement;
}
/**
* Reset leave management list by adding a new leave management line with negative quantity.
*
* @param leaveLine
* @param user
* @param comments
* @param date
* @param fromDate
* @param toDate
*/
@Transactional
public void reset(
LeaveLine leaveLine,
User user,
String comments,
LocalDate date,
LocalDate fromDate,
LocalDate toDate) {
LeaveManagement leaveManagement =
createLeaveManagement(
leaveLine, user, comments, date, fromDate, toDate, leaveLine.getQuantity().negate());
leaveLine.addLeaveManagementListItem(leaveManagement);
leaveLine.setQuantity(BigDecimal.ZERO);
leaveLine.setTotalQuantity(BigDecimal.ZERO);
}
}

View File

@ -0,0 +1,30 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.lunch.voucher;
import com.axelor.apps.hr.db.LunchVoucherAdvance;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
public interface LunchVoucherAdvanceService {
@Transactional(rollbackOn = {Exception.class})
public void onNewAdvance(LunchVoucherAdvance lunchVoucherAdvance) throws AxelorException;
public int useAdvance(LunchVoucherAdvance lunchVoucherAdvance, int qty) throws AxelorException;
}

View File

@ -0,0 +1,67 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.lunch.voucher;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.LunchVoucherAdvance;
import com.axelor.apps.hr.db.repo.HRConfigRepository;
import com.axelor.apps.hr.db.repo.LunchVoucherAdvanceRepository;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
public class LunchVoucherAdvanceServiceImpl implements LunchVoucherAdvanceService {
protected HRConfigService hrConfigService;
@Inject
public LunchVoucherAdvanceServiceImpl(HRConfigService hrConfigService) {
this.hrConfigService = hrConfigService;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void onNewAdvance(LunchVoucherAdvance lunchVoucherAdvance) throws AxelorException {
HRConfig config =
hrConfigService.getHRConfig(
lunchVoucherAdvance.getEmployee().getMainEmploymentContract().getPayCompany());
config.setAvailableStockLunchVoucher(
config.getAvailableStockLunchVoucher() - lunchVoucherAdvance.getNbrLunchVouchers());
Beans.get(LunchVoucherAdvanceRepository.class).save(lunchVoucherAdvance);
Beans.get(HRConfigRepository.class).save(config);
}
@Override
public int useAdvance(LunchVoucherAdvance lunchVoucherAdvance, int qty) throws AxelorException {
int toUse =
lunchVoucherAdvance.getNbrLunchVouchers() - lunchVoucherAdvance.getNbrLunchVouchersUsed();
if (qty > toUse) {
lunchVoucherAdvance.setNbrLunchVouchersUsed(lunchVoucherAdvance.getNbrLunchVouchers());
return qty - toUse;
}
lunchVoucherAdvance.setNbrLunchVouchersUsed(
lunchVoucherAdvance.getNbrLunchVouchersUsed() + qty);
return 0;
}
}

View File

@ -0,0 +1,46 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.lunch.voucher;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.LunchVoucherMgt;
import com.axelor.apps.hr.db.LunchVoucherMgtLine;
import com.axelor.exception.AxelorException;
public interface LunchVoucherMgtLineService {
public LunchVoucherMgtLine create(Employee employee, LunchVoucherMgt lunchVoucherMgt)
throws AxelorException;
/**
* Set the lunch voucher format in the line. If the format in employee is null, uses format from
* HR configuration.
*
* @param employee
* @param lunchVoucherMgt
* @param lunchVoucherMgtLine @throws AxelorException
*/
void fillLunchVoucherFormat(
Employee employee, LunchVoucherMgt lunchVoucherMgt, LunchVoucherMgtLine lunchVoucherMgtLine)
throws AxelorException;
public void compute(LunchVoucherMgtLine lunchVoucherMgtLine) throws AxelorException;
public void computeAllAttrs(
Employee employee, LunchVoucherMgt lunchVoucherMgt, LunchVoucherMgtLine lunchVoucherMgtLine);
}

View File

@ -0,0 +1,121 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.lunch.voucher;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.LunchVoucherAdvance;
import com.axelor.apps.hr.db.LunchVoucherMgt;
import com.axelor.apps.hr.db.LunchVoucherMgtLine;
import com.axelor.apps.hr.db.repo.LunchVoucherAdvanceRepository;
import com.axelor.apps.hr.db.repo.LunchVoucherMgtLineRepository;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.hr.service.employee.EmployeeService;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import java.math.RoundingMode;
import java.util.List;
public class LunchVoucherMgtLineServiceImpl implements LunchVoucherMgtLineService {
@Inject protected EmployeeService employeeService;
/*
* Create a new line from employee and lunchVoucherMgt
*/
@Override
public LunchVoucherMgtLine create(Employee employee, LunchVoucherMgt lunchVoucherMgt)
throws AxelorException {
LunchVoucherMgtLine lunchVoucherMgtLine = new LunchVoucherMgtLine();
lunchVoucherMgtLine.setEmployee(employee);
computeAllAttrs(employee, lunchVoucherMgt, lunchVoucherMgtLine);
return lunchVoucherMgtLine;
}
/*
* Try to set the line attributes: if an exception occurs, the line status
* is anomaly.
*/
@Override
public void computeAllAttrs(
Employee employee, LunchVoucherMgt lunchVoucherMgt, LunchVoucherMgtLine lunchVoucherMgtLine) {
Integer lineStatus = LunchVoucherMgtLineRepository.STATUS_CALCULATED;
try {
lunchVoucherMgtLine.setInAdvanceNbr(computeEmployeeLunchVoucherAdvance(employee));
lunchVoucherMgtLine.setDaysWorkedNbr(
employeeService
.getDaysWorkedInPeriod(
employee,
lunchVoucherMgt.getLeavePeriod().getFromDate(),
lunchVoucherMgt.getLeavePeriod().getToDate())
.setScale(0, RoundingMode.HALF_UP)
.intValue());
compute(lunchVoucherMgtLine);
fillLunchVoucherFormat(employee, lunchVoucherMgt, lunchVoucherMgtLine);
} catch (Exception e) {
TraceBackService.trace(e);
lineStatus = LunchVoucherMgtLineRepository.STATUS_ANOMALY;
}
lunchVoucherMgtLine.setStatusSelect(lineStatus);
}
private Integer computeEmployeeLunchVoucherAdvance(Employee employee) {
int number = 0;
List<LunchVoucherAdvance> list =
Beans.get(LunchVoucherAdvanceRepository.class)
.all()
.filter(
"self.employee.id = ?1 AND self.nbrLunchVouchersUsed < self.nbrLunchVouchers",
employee.getId())
.fetch();
for (LunchVoucherAdvance item : list) {
number += item.getNbrLunchVouchers() - item.getNbrLunchVouchersUsed();
}
return number;
}
@Override
public void fillLunchVoucherFormat(
Employee employee, LunchVoucherMgt lunchVoucherMgt, LunchVoucherMgtLine lunchVoucherMgtLine)
throws AxelorException {
int employeeFormat = employee.getLunchVoucherFormatSelect();
if (employeeFormat != 0) {
lunchVoucherMgtLine.setLunchVoucherFormatSelect(employeeFormat);
} else {
Company company = lunchVoucherMgt.getCompany();
HRConfig hrConfig = Beans.get(HRConfigService.class).getHRConfig(company);
lunchVoucherMgtLine.setLunchVoucherFormatSelect(hrConfig.getLunchVoucherFormatSelect());
}
}
@Override
public void compute(LunchVoucherMgtLine lunchVoucherMgtLine) throws AxelorException {
Integer lunchVoucherNumber =
lunchVoucherMgtLine.getDaysWorkedNbr()
- (lunchVoucherMgtLine.getCanteenEntries()
+ lunchVoucherMgtLine.getDaysOverseas()
+ lunchVoucherMgtLine.getInAdvanceNbr()
+ lunchVoucherMgtLine.getInvitation());
lunchVoucherMgtLine.setLunchVoucherNumber(Integer.max(lunchVoucherNumber, 0));
}
}

View File

@ -0,0 +1,49 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.lunch.voucher;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.hr.db.LunchVoucherMgt;
import com.axelor.apps.hr.db.LunchVoucherMgtLine;
import com.axelor.exception.AxelorException;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.util.List;
public interface LunchVoucherMgtService {
@Transactional(rollbackOn = {Exception.class})
public void calculate(LunchVoucherMgt lunchVoucherMgt) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void validate(LunchVoucherMgt lunchVoucherMgt) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public int updateStock(
List<LunchVoucherMgtLine> newLunchVoucherMgtLines,
List<LunchVoucherMgtLine> oldLunchVoucherMgtLines,
Company company)
throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void export(LunchVoucherMgt lunchVoucherMgt) throws IOException;
public int checkStock(Company company, int numberToUse) throws AxelorException;
public void calculateTotal(LunchVoucherMgt lunchVoucherMgt);
}

View File

@ -0,0 +1,291 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.lunch.voucher;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.*;
import com.axelor.apps.hr.db.repo.*;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.common.ObjectUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.common.base.Joiner;
import com.google.common.base.Throwables;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class LunchVoucherMgtServiceImpl implements LunchVoucherMgtService {
protected LunchVoucherMgtRepository lunchVoucherMgtRepository;
protected LunchVoucherMgtLineService lunchVoucherMgtLineService;
protected LunchVoucherAdvanceService lunchVoucherAdvanceService;
protected HRConfigService hrConfigService;
@Inject
public LunchVoucherMgtServiceImpl(
LunchVoucherMgtLineService lunchVoucherMgtLineService,
LunchVoucherAdvanceService lunchVoucherAdvanceService,
LunchVoucherMgtRepository lunchVoucherMgtRepository,
HRConfigService hrConfigService) {
this.lunchVoucherMgtLineService = lunchVoucherMgtLineService;
this.lunchVoucherMgtRepository = lunchVoucherMgtRepository;
this.lunchVoucherAdvanceService = lunchVoucherAdvanceService;
this.hrConfigService = hrConfigService;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void calculate(LunchVoucherMgt lunchVoucherMgt) throws AxelorException {
Company company = lunchVoucherMgt.getCompany();
if (company == null) {
throw new AxelorException(
lunchVoucherMgt,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get("Please fill a company."));
}
if (lunchVoucherMgt.getLeavePeriod() == null) {
throw new AxelorException(
lunchVoucherMgt,
TraceBackRepository.CATEGORY_MISSING_FIELD,
I18n.get("Please fill a leave period."));
}
HRConfig hrConfig = hrConfigService.getHRConfig(company);
List<Employee> employeeList =
Beans.get(EmployeeRepository.class)
.all()
.filter("self.mainEmploymentContract.payCompany = ?1", company)
.fetch();
for (Employee employee : employeeList) {
if (employee != null) {
LunchVoucherMgtLine lunchVoucherMgtLine = obtainLineFromEmployee(employee, lunchVoucherMgt);
// the employee doesn't have a line, create it
if (lunchVoucherMgtLine == null) {
lunchVoucherMgtLine = lunchVoucherMgtLineService.create(employee, lunchVoucherMgt);
lunchVoucherMgt.addLunchVoucherMgtLineListItem(lunchVoucherMgtLine);
}
// the line exist and is not already calculated, update it
else {
if (lunchVoucherMgtLine.getStatusSelect()
!= LunchVoucherMgtLineRepository.STATUS_CALCULATED) {
lunchVoucherMgtLineService.computeAllAttrs(
employee, lunchVoucherMgt, lunchVoucherMgtLine);
}
}
}
}
lunchVoucherMgt.setStatusSelect(LunchVoucherMgtRepository.STATUS_CALCULATED);
lunchVoucherMgt.setStockQuantityStatus(hrConfig.getAvailableStockLunchVoucher());
calculateTotal(lunchVoucherMgt);
lunchVoucherMgtRepository.save(lunchVoucherMgt);
}
protected LunchVoucherMgtLine obtainLineFromEmployee(
Employee employee, LunchVoucherMgt lunchVoucherMgt) {
for (LunchVoucherMgtLine line : lunchVoucherMgt.getLunchVoucherMgtLineList()) {
if (line.getEmployee() == employee) {
return line;
}
}
return null;
}
@Override
public void calculateTotal(LunchVoucherMgt lunchVoucherMgt) {
List<LunchVoucherMgtLine> lunchVoucherMgtLineList =
lunchVoucherMgt.getLunchVoucherMgtLineList();
int total = 0;
int totalInAdvance = 0;
int totalGiven = 0;
if (!ObjectUtils.isEmpty(lunchVoucherMgtLineList)) {
for (LunchVoucherMgtLine lunchVoucherMgtLine : lunchVoucherMgtLineList) {
total += lunchVoucherMgtLine.getLunchVoucherNumber();
totalInAdvance += lunchVoucherMgtLine.getInAdvanceNbr();
totalGiven += lunchVoucherMgtLine.getGivenToEmployee();
}
}
lunchVoucherMgt.setTotalLunchVouchers(
total + totalInAdvance + lunchVoucherMgt.getStockLineQuantity());
lunchVoucherMgt.setRequestedLunchVouchers(total + lunchVoucherMgt.getStockLineQuantity());
lunchVoucherMgt.setGivenLunchVouchers(totalGiven);
}
@Override
public int checkStock(Company company, int numberToUse) throws AxelorException {
HRConfig hrConfig = hrConfigService.getHRConfig(company);
int minStoclLV = hrConfig.getMinStockLunchVoucher();
int availableStoclLV = hrConfig.getAvailableStockLunchVoucher();
return availableStoclLV - numberToUse - minStoclLV;
}
/**
* Update the stock in the config from lunch voucher management
*
* @param newLunchVoucherMgtLines the new mgt lines
* @param oldLunchVoucherMgtLines the previous mgt lines
* @param company the company of the HR config
* @return the stock quantity status of the lunch voucher mgt
* @throws AxelorException
*/
@Transactional(rollbackOn = {Exception.class})
@Override
public int updateStock(
List<LunchVoucherMgtLine> newLunchVoucherMgtLines,
List<LunchVoucherMgtLine> oldLunchVoucherMgtLines,
Company company)
throws AxelorException {
HRConfig hrConfig = hrConfigService.getHRConfig(company);
int newLunchVoucherQty = hrConfig.getAvailableStockLunchVoucher();
int i = 0;
for (LunchVoucherMgtLine line : newLunchVoucherMgtLines) {
int oldQty = oldLunchVoucherMgtLines.get(i).getGivenToEmployee();
int newQty = line.getGivenToEmployee();
newLunchVoucherQty = newLunchVoucherQty - newQty + oldQty;
i++;
}
hrConfig.setAvailableStockLunchVoucher(newLunchVoucherQty);
Beans.get(HRConfigRepository.class).save(hrConfig);
return hrConfig.getAvailableStockLunchVoucher();
}
@Transactional(rollbackOn = {Exception.class})
public void export(LunchVoucherMgt lunchVoucherMgt) throws IOException {
MetaFile metaFile = new MetaFile();
metaFile.setFileName(
I18n.get("LunchVoucherCommand")
+ " - "
+ LocalDate.now().format(DateTimeFormatter.ISO_DATE)
+ ".csv");
Path tempFile = MetaFiles.createTempFile(null, ".csv");
final OutputStream os = new FileOutputStream(tempFile.toFile());
try (final Writer writer = new OutputStreamWriter(os)) {
List<String> header = new ArrayList<>();
header.add(escapeCsv(I18n.get("Company code")));
header.add(escapeCsv(I18n.get("Lunch Voucher's number")));
header.add(escapeCsv(I18n.get("Employee")));
header.add(escapeCsv(I18n.get("Lunch Voucher format")));
writer.write(Joiner.on(";").join(header));
for (LunchVoucherMgtLine lunchVoucherMgtLine : lunchVoucherMgt.getLunchVoucherMgtLineList()) {
List<String> line = new ArrayList<>();
line.add(escapeCsv(lunchVoucherMgt.getCompany().getCode()));
line.add(escapeCsv(lunchVoucherMgtLine.getLunchVoucherNumber().toString()));
line.add(escapeCsv(lunchVoucherMgtLine.getEmployee().getName()));
line.add(
escapeCsv(lunchVoucherMgtLine.getEmployee().getLunchVoucherFormatSelect().toString()));
writer.write("\n");
writer.write(Joiner.on(";").join(line));
}
Beans.get(MetaFiles.class).upload(tempFile.toFile(), metaFile);
} catch (Exception e) {
Throwables.propagate(e);
} finally {
Files.deleteIfExists(tempFile);
}
/*
*/
// lunchVoucherMgt.setExported(true);
lunchVoucherMgt.setCsvFile(metaFile);
lunchVoucherMgt.setExportDate(Beans.get(AppBaseService.class).getTodayDate());
lunchVoucherMgtRepository.save(lunchVoucherMgt);
}
protected String escapeCsv(String value) {
if (value == null) return "";
if (value.indexOf('"') > -1) value = value.replaceAll("\"", "\"\"");
return '"' + value + '"';
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void validate(LunchVoucherMgt lunchVoucherMgt) throws AxelorException {
Company company = lunchVoucherMgt.getCompany();
HRConfig hrConfig = hrConfigService.getHRConfig(company);
LunchVoucherAdvanceRepository advanceRepo = Beans.get(LunchVoucherAdvanceRepository.class);
for (LunchVoucherMgtLine item : lunchVoucherMgt.getLunchVoucherMgtLineList()) {
if (item.getInAdvanceNbr() > 0) {
int qtyToUse = item.getInAdvanceNbr();
List<LunchVoucherAdvance> list =
advanceRepo
.all()
.filter(
"self.employee.id = ?1 AND self.nbrLunchVouchersUsed < self.nbrLunchVouchers",
item.getEmployee().getId())
.order("distributionDate")
.fetch();
for (LunchVoucherAdvance subItem : list) {
qtyToUse = lunchVoucherAdvanceService.useAdvance(subItem, qtyToUse);
advanceRepo.save(subItem);
if (qtyToUse <= 0) {
break;
}
}
}
}
hrConfig.setAvailableStockLunchVoucher(
hrConfig.getAvailableStockLunchVoucher() + lunchVoucherMgt.getStockLineQuantity());
lunchVoucherMgt.setStatusSelect(LunchVoucherMgtRepository.STATUS_VALIDATED);
Beans.get(HRConfigRepository.class).save(hrConfig);
lunchVoucherMgtRepository.save(lunchVoucherMgt);
}
}

View File

@ -0,0 +1,36 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.project;
import com.axelor.apps.project.db.Project;
import com.axelor.exception.AxelorException;
import com.axelor.team.db.TeamTask;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
public interface ProjectPlanningTimeService {
public BigDecimal getTaskPlannedHrs(TeamTask teamTask);
public BigDecimal getProjectPlannedHrs(Project project);
public void addMultipleProjectPlanningTime(Map<String, Object> dataMap) throws AxelorException;
public void removeProjectPlanningLines(List<Map<String, Object>> projectPlanningLines);
}

View File

@ -0,0 +1,214 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.project;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.weeklyplanning.WeeklyPlanningService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.service.publicHoliday.PublicHolidayHrService;
import com.axelor.apps.project.db.Project;
import com.axelor.apps.project.db.ProjectPlanningTime;
import com.axelor.apps.project.db.repo.ProjectPlanningTimeRepository;
import com.axelor.apps.project.db.repo.ProjectRepository;
import com.axelor.auth.db.User;
import com.axelor.auth.db.repo.UserRepository;
import com.axelor.exception.AxelorException;
import com.axelor.team.db.TeamTask;
import com.axelor.team.db.repo.TeamTaskRepository;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProjectPlanningTimeServiceImpl implements ProjectPlanningTimeService {
protected static final Logger LOG = LoggerFactory.getLogger(ProjectPlanningTimeService.class);
protected ProjectPlanningTimeRepository planningTimeRepo;
protected ProjectRepository projectRepo;
protected TeamTaskRepository teamTaskRepo;
protected WeeklyPlanningService weeklyPlanningService;
protected PublicHolidayHrService holidayService;
protected ProductRepository productRepo;
protected UserRepository userRepo;
@Inject
public ProjectPlanningTimeServiceImpl(
ProjectPlanningTimeRepository planningTimeRepo,
ProjectRepository projectRepo,
TeamTaskRepository teamTaskRepo,
WeeklyPlanningService weeklyPlanningService,
PublicHolidayHrService holidayService,
ProductRepository productRepo,
UserRepository userRepo) {
super();
this.planningTimeRepo = planningTimeRepo;
this.projectRepo = projectRepo;
this.teamTaskRepo = teamTaskRepo;
this.weeklyPlanningService = weeklyPlanningService;
this.holidayService = holidayService;
this.productRepo = productRepo;
this.userRepo = userRepo;
}
@Override
public BigDecimal getTaskPlannedHrs(TeamTask task) {
BigDecimal totalPlanned = BigDecimal.ZERO;
if (task != null) {
List<ProjectPlanningTime> plannings =
planningTimeRepo.all().filter("self.task = ?1", task).fetch();
if (plannings != null) {
totalPlanned =
plannings
.stream()
.map(ProjectPlanningTime::getPlannedHours)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
return totalPlanned;
}
@Override
public BigDecimal getProjectPlannedHrs(Project project) {
BigDecimal totalPlanned = BigDecimal.ZERO;
if (project != null) {
List<ProjectPlanningTime> plannings =
planningTimeRepo
.all()
.filter(
"self.project = ?1 OR (self.project.parentProject = ?1 AND self.project.parentProject.isShowPhasesElements = ?2)",
project,
true)
.fetch();
if (plannings != null) {
totalPlanned =
plannings
.stream()
.map(ProjectPlanningTime::getPlannedHours)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
return totalPlanned;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void addMultipleProjectPlanningTime(Map<String, Object> datas) throws AxelorException {
if (datas.get("project") == null
|| datas.get("user") == null
|| datas.get("fromDate") == null
|| datas.get("toDate") == null) {
return;
}
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
LocalDateTime fromDate = LocalDateTime.parse(datas.get("fromDate").toString(), formatter);
LocalDateTime toDate = LocalDateTime.parse(datas.get("toDate").toString(), formatter);
TeamTask teamTask = null;
Map<String, Object> objMap = (Map) datas.get("project");
Project project = projectRepo.find(Long.parseLong(objMap.get("id").toString()));
Integer timePercent = 0;
if (datas.get("timepercent") != null) {
timePercent = Integer.parseInt(datas.get("timepercent").toString());
}
objMap = (Map) datas.get("user");
User user = userRepo.find(Long.parseLong(objMap.get("id").toString()));
if (user.getEmployee() == null) {
return;
}
if (datas.get("task") != null) {
objMap = (Map) datas.get("task");
teamTask = teamTaskRepo.find(Long.valueOf(objMap.get("id").toString()));
}
Product activity = null;
if (datas.get("product") != null) {
objMap = (Map) datas.get("product");
activity = productRepo.find(Long.valueOf(objMap.get("id").toString()));
}
Employee employee = user.getEmployee();
BigDecimal dailyWorkHrs = employee.getDailyWorkHours();
while (fromDate.isBefore(toDate)) {
LocalDate date = fromDate.toLocalDate();
LOG.debug("Create Planning for the date: {}", date);
double dayHrs = 0;
if (employee.getWeeklyPlanning() != null) {
dayHrs = weeklyPlanningService.getWorkingDayValueInDays(employee.getWeeklyPlanning(), date);
}
if (dayHrs > 0 && !holidayService.checkPublicHolidayDay(date, employee)) {
ProjectPlanningTime planningTime = new ProjectPlanningTime();
planningTime.setTask(teamTask);
planningTime.setProduct(activity);
planningTime.setTimepercent(timePercent);
planningTime.setUser(user);
planningTime.setDate(date);
planningTime.setProject(project);
planningTime.setIsIncludeInTurnoverForecast(
(Boolean) datas.get("isIncludeInTurnoverForecast"));
BigDecimal totalHours = BigDecimal.ZERO;
if (timePercent > 0) {
totalHours =
dailyWorkHrs.multiply(new BigDecimal(timePercent)).divide(new BigDecimal(100));
}
planningTime.setPlannedHours(totalHours);
planningTimeRepo.save(planningTime);
}
fromDate = fromDate.plusDays(1);
}
}
@Override
@Transactional
public void removeProjectPlanningLines(List<Map<String, Object>> projectPlanningLines) {
for (Map<String, Object> line : projectPlanningLines) {
ProjectPlanningTime projectPlanningTime =
planningTimeRepo.find(Long.parseLong(line.get("id").toString()));
planningTimeRepo.remove(projectPlanningTime);
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.publicHoliday;
import com.axelor.apps.base.db.EventsPlanning;
import com.axelor.apps.base.db.EventsPlanningLine;
import com.axelor.apps.base.db.repo.EventsPlanningLineRepository;
import com.axelor.apps.base.service.publicHoliday.PublicHolidayService;
import com.axelor.apps.base.service.weeklyplanning.WeeklyPlanningService;
import com.axelor.apps.hr.db.Employee;
import com.google.inject.Inject;
import java.time.LocalDate;
import java.util.List;
public class PublicHolidayHrService extends PublicHolidayService {
@Inject
public PublicHolidayHrService(
WeeklyPlanningService weeklyPlanningService,
EventsPlanningLineRepository eventsPlanningLineRepo) {
super(weeklyPlanningService, eventsPlanningLineRepo);
}
public boolean checkPublicHolidayDay(LocalDate date, Employee employee) {
return super.checkPublicHolidayDay(date, employee.getPublicHolidayEventsPlanning());
}
public int getImposedDayNumber(Employee employee, LocalDate startDate, LocalDate endDate) {
EventsPlanning imposedDays = employee.getImposedDayEventsPlanning();
if (imposedDays == null
|| imposedDays.getEventsPlanningLineList() == null
|| imposedDays.getEventsPlanningLineList().isEmpty()) {
return 0;
}
List<EventsPlanningLine> imposedDayList =
eventsPlanningLineRepo
.all()
.filter(
"self.eventsPlanning = ?1 AND self.date BETWEEN ?2 AND ?3",
imposedDays,
startDate,
endDate)
.fetch();
return imposedDayList.size();
}
}

View File

@ -0,0 +1,122 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.timesheet;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.project.db.Project;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
public interface TimesheetLineService {
/**
* Compute duration from hours or to hours.
*
* @param timesheet a timesheet containing the time preference.
* @param duration the duration to be converted.
* @param toHours if we convert the duration to hours, or if we convert from hours.
* @return the computed duration.
* @throws AxelorException
*/
BigDecimal computeHoursDuration(Timesheet timesheet, BigDecimal duration, boolean toHours)
throws AxelorException;
/**
* Compute duration from hours or to hours.
*
* @param timePref a select containing the type of the duration. Can be minute, hours or days.
* @param duration the duration to be converted
* @param dailyWorkHrs The hours of work during a day.
* @param toHours if we convert the duration to hours, or if we convert from hours.
* @return the computed duration.
* @throws AxelorException
*/
BigDecimal computeHoursDuration(
String timePref, BigDecimal duration, BigDecimal dailyWorkHrs, boolean toHours)
throws AxelorException;
/**
* Creates a timesheet line.
*
* @param project
* @param product
* @param user
* @param date
* @param timesheet
* @param hours
* @param comments
* @return the created timesheet line.
*/
TimesheetLine createTimesheetLine(
Project project,
Product product,
User user,
LocalDate date,
Timesheet timesheet,
BigDecimal hours,
String comments);
/**
* Creates a timesheet line without project and product. Used to generate timesheet lines for
* holidays or day leaves.
*
* @param user
* @param date
* @param timesheet
* @param hours
* @param comments
* @return the created timesheet line.
*/
TimesheetLine createTimesheetLine(
User user, LocalDate date, Timesheet timesheet, BigDecimal hours, String comments);
TimesheetLine updateTimesheetLine(
TimesheetLine timesheetLine,
Project project,
Product product,
User user,
LocalDate date,
Timesheet timesheet,
BigDecimal hours,
String comments);
/**
* Compute the total duration of timesheet lines. Add each duration only if the timesheet is
* validated or confirmed.
*
* @param timesheetLineList a list of timesheet lines.
* @return a {@link java.time.Duration}.
*/
Duration computeTotalDuration(List<TimesheetLine> timesheetLineList);
/**
* Calculates time spent on the project base on timesheet lines for the validated {@link
* Timesheet}.
*
* @param timesheetLineList
* @return {@link Map}
*/
Map<Project, BigDecimal> getProjectTimeSpentMap(List<TimesheetLine> timesheetLineList);
}

View File

@ -0,0 +1,285 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.timesheet;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.TimesheetHRRepository;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.project.db.Project;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TimesheetLineServiceImpl implements TimesheetLineService {
@Inject private TimesheetService timesheetService;
@Inject private TimesheetHRRepository timesheetHRRepository;
@Inject private TimesheetRepository timesheetRepository;
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
public BigDecimal computeHoursDuration(Timesheet timesheet, BigDecimal duration, boolean toHours)
throws AxelorException {
if (duration == null) {
return null;
}
AppBaseService appBaseService = Beans.get(AppBaseService.class);
BigDecimal dailyWorkHrs;
String timePref;
log.debug(
"Get user duration for duration: {}, timesheet: {}",
duration,
timesheet == null ? "null" : timesheet.getFullName());
if (timesheet != null) {
User user = timesheet.getUser();
Employee employee = user.getEmployee();
log.debug("Employee: {}", employee);
timePref = timesheet.getTimeLoggingPreferenceSelect();
if (employee != null) {
dailyWorkHrs = employee.getDailyWorkHours();
if (timePref == null) {
timePref = employee.getTimeLoggingPreferenceSelect();
}
} else {
dailyWorkHrs = appBaseService.getAppBase().getDailyWorkHours();
}
} else {
timePref = appBaseService.getAppBase().getTimeLoggingPreferenceSelect();
dailyWorkHrs = appBaseService.getAppBase().getDailyWorkHours();
}
return computeHoursDuration(timePref, duration, dailyWorkHrs, toHours);
}
@Override
public BigDecimal computeHoursDuration(
String timePref, BigDecimal duration, BigDecimal dailyWorkHrs, boolean toHours)
throws AxelorException {
log.debug("Timesheet time pref: {}, Daily Working hours: {}", timePref, dailyWorkHrs);
if (timePref == null) {
return duration;
}
if (toHours) {
duration = computeDurationToHours(timePref, duration, dailyWorkHrs);
} else {
duration = computeDurationFromHours(timePref, duration, dailyWorkHrs);
}
log.debug("Calculated duration: {}", duration);
return duration;
}
protected BigDecimal computeDurationToHours(
String timePref, BigDecimal duration, BigDecimal dailyWorkHrs) throws AxelorException {
switch (timePref) {
case EmployeeRepository.TIME_PREFERENCE_DAYS:
if (dailyWorkHrs.compareTo(BigDecimal.ZERO) == 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.TIMESHEET_DAILY_WORK_HOURS));
}
return duration.multiply(dailyWorkHrs);
case EmployeeRepository.TIME_PREFERENCE_MINUTES:
return duration.divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);
default:
return duration;
}
}
protected BigDecimal computeDurationFromHours(
String timePref, BigDecimal duration, BigDecimal dailyWorkHrs) throws AxelorException {
switch (timePref) {
case EmployeeRepository.TIME_PREFERENCE_DAYS:
if (dailyWorkHrs.compareTo(BigDecimal.ZERO) == 0) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.TIMESHEET_DAILY_WORK_HOURS));
}
return duration.divide(dailyWorkHrs, 2, RoundingMode.HALF_UP);
case EmployeeRepository.TIME_PREFERENCE_MINUTES:
return duration.multiply(new BigDecimal(60));
default:
return duration;
}
}
@Override
public TimesheetLine createTimesheetLine(
Project project,
Product product,
User user,
LocalDate date,
Timesheet timesheet,
BigDecimal hours,
String comments) {
TimesheetLine timesheetLine = new TimesheetLine();
timesheetLine.setDate(date);
timesheetLine.setComments(comments);
timesheetLine.setProduct(product);
timesheetLine.setProject(project);
timesheetLine.setUser(user);
timesheetLine.setHoursDuration(hours);
try {
timesheetLine.setDuration(computeHoursDuration(timesheet, hours, false));
} catch (AxelorException e) {
log.error(e.getLocalizedMessage());
TraceBackService.trace(e);
}
timesheet.addTimesheetLineListItem(timesheetLine);
return timesheetLine;
}
@Override
public TimesheetLine createTimesheetLine(
User user, LocalDate date, Timesheet timesheet, BigDecimal hours, String comments) {
return createTimesheetLine(null, null, user, date, timesheet, hours, comments);
}
@Override
public TimesheetLine updateTimesheetLine(
TimesheetLine timesheetLine,
Project project,
Product product,
User user,
LocalDate date,
Timesheet timesheet,
BigDecimal hours,
String comments) {
timesheetLine.setDate(date);
timesheetLine.setComments(comments);
timesheetLine.setProduct(product);
timesheetLine.setProject(project);
timesheetLine.setUser(user);
timesheetLine.setHoursDuration(hours);
try {
timesheetLine.setDuration(computeHoursDuration(timesheet, hours, false));
} catch (AxelorException e) {
log.error(e.getLocalizedMessage());
TraceBackService.trace(e);
}
timesheet.addTimesheetLineListItem(timesheetLine);
return timesheetLine;
}
@Override
public Duration computeTotalDuration(List<TimesheetLine> timesheetLineList) {
if (timesheetLineList == null || timesheetLineList.isEmpty()) {
return Duration.ZERO;
}
long totalSecDuration = 0L;
for (TimesheetLine timesheetLine : timesheetLineList) {
// if null, that means the timesheet line is just created so the parent is not canceled.
if (timesheetLine.getTimesheet() == null
|| timesheetLine.getTimesheet().getStatusSelect()
!= TimesheetRepository.STATUS_CANCELED) {
totalSecDuration +=
timesheetLine.getHoursDuration().multiply(new BigDecimal("3600")).longValue();
}
}
return Duration.ofSeconds(totalSecDuration);
}
@Override
public Map<Project, BigDecimal> getProjectTimeSpentMap(List<TimesheetLine> timesheetLineList) {
Map<Project, BigDecimal> projectTimeSpentMap = new HashMap<>();
if (timesheetLineList != null) {
for (TimesheetLine timesheetLine : timesheetLineList) {
Project project = timesheetLine.getProject();
BigDecimal hoursDuration = timesheetLine.getHoursDuration();
if (project != null) {
if (projectTimeSpentMap.containsKey(project)) {
hoursDuration = hoursDuration.add(projectTimeSpentMap.get(project));
}
projectTimeSpentMap.put(project, hoursDuration);
}
}
}
return projectTimeSpentMap;
}
@Transactional
public TimesheetLine setTimesheet(TimesheetLine timesheetLine) {
Timesheet timesheet =
timesheetRepository
.all()
.filter(
"self.user = ?1 AND self.company = ?2 AND (self.statusSelect = 1 OR self.statusSelect = 2) AND ((?3 BETWEEN self.fromDate AND self.toDate) OR (self.toDate = null)) ORDER BY self.id ASC",
timesheetLine.getUser(),
timesheetLine.getProject().getCompany(),
timesheetLine.getDate())
.fetchOne();
if (timesheet == null) {
Timesheet lastTimesheet =
timesheetRepository
.all()
.filter(
"self.user = ?1 AND self.statusSelect != ?2 ORDER BY self.toDate DESC",
timesheetLine.getUser(),
TimesheetRepository.STATUS_CANCELED)
.fetchOne();
timesheet =
timesheetService.createTimesheet(
timesheetLine.getUser(),
lastTimesheet != null && lastTimesheet.getToDate() != null
? lastTimesheet.getToDate().plusDays(1)
: timesheetLine.getDate(),
null);
timesheet = timesheetHRRepository.save(timesheet);
}
timesheetLine.setTimesheet(timesheet);
return timesheetLine;
}
}

View File

@ -0,0 +1,40 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.timesheet;
import com.axelor.apps.hr.db.TimesheetReport;
import com.axelor.apps.message.db.Message;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface TimesheetReportService {
static TimesheetReportService getInstance() {
return Beans.get(TimesheetReportService.class);
}
Set<User> getUserToBeReminded(TimesheetReport timesheetReport);
List<Message> sendReminders(TimesheetReport timesheetReport) throws AxelorException;
List<Map<String, Object>> getTimesheetReportList(String TimesheetReportId);
}

View File

@ -0,0 +1,502 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.timesheet;
import com.axelor.apps.base.db.DayPlanning;
import com.axelor.apps.base.db.WeeklyPlanning;
import com.axelor.apps.base.service.publicHoliday.PublicHolidayService;
import com.axelor.apps.base.service.weeklyplanning.WeeklyPlanningService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.LeaveRequest;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.hr.db.TimesheetReminder;
import com.axelor.apps.hr.db.TimesheetReminderLine;
import com.axelor.apps.hr.db.TimesheetReport;
import com.axelor.apps.hr.db.repo.ExtraHoursLineRepository;
import com.axelor.apps.hr.db.repo.ExtraHoursRepository;
import com.axelor.apps.hr.db.repo.TimesheetLineRepository;
import com.axelor.apps.hr.db.repo.TimesheetReminderRepository;
import com.axelor.apps.hr.db.repo.TimesheetReportRepository;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.app.AppHumanResourceService;
import com.axelor.apps.hr.service.employee.EmployeeService;
import com.axelor.apps.hr.service.leave.LeaveService;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.db.Template;
import com.axelor.apps.message.service.MessageService;
import com.axelor.apps.message.service.TemplateMessageService;
import com.axelor.apps.tool.QueryBuilder;
import com.axelor.apps.tool.date.DateTool;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
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.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.collections4.CollectionUtils;
public class TimesheetReportServiceImpl implements TimesheetReportService {
protected TimesheetReminderRepository timesheetReminderRepo;
protected TimesheetReportRepository timesheetReportRepository;
protected ExtraHoursLineRepository extraHoursLineRepository;
protected TimesheetLineRepository timesheetLineRepository;
protected MessageService messageService;
protected TemplateMessageService templateMessageService;
protected PublicHolidayService publicHolidayService;
protected WeeklyPlanningService weeklyPlanningService;
protected EmployeeService employeeService;
protected TimesheetLineService timesheetLineService;
protected LeaveService leaveService;
@Inject
public TimesheetReportServiceImpl(
TimesheetReminderRepository timesheetReminderRepo,
TimesheetReportRepository timesheetReportRepository,
ExtraHoursLineRepository extraHoursLineRepository,
TimesheetLineRepository timesheetLineRepository,
MessageService messageService,
TemplateMessageService templateMessageService,
PublicHolidayService publicHolidayService,
WeeklyPlanningService weeklyPlanningService,
EmployeeService employeeService,
TimesheetLineService timesheetLineService,
LeaveService leaveService) {
this.timesheetReminderRepo = timesheetReminderRepo;
this.timesheetReportRepository = timesheetReportRepository;
this.extraHoursLineRepository = extraHoursLineRepository;
this.timesheetLineRepository = timesheetLineRepository;
this.messageService = messageService;
this.templateMessageService = templateMessageService;
this.publicHolidayService = publicHolidayService;
this.weeklyPlanningService = weeklyPlanningService;
this.employeeService = employeeService;
this.timesheetLineService = timesheetLineService;
this.leaveService = leaveService;
}
@Override
public Set<User> getUserToBeReminded(TimesheetReport timesheetReport) {
Set<User> userSet = new HashSet<>();
BigDecimal worksHour = BigDecimal.ZERO, workedHour = BigDecimal.ZERO;
List<User> users = getUsers(timesheetReport);
LocalDate fromDate = timesheetReport.getFromDate();
LocalDate toDate = timesheetReport.getToDate();
for (User user : users) {
Employee employee = user.getEmployee();
try {
worksHour = workedHour = BigDecimal.ZERO;
BigDecimal publicHolidays =
publicHolidayService.computePublicHolidayDays(
fromDate,
toDate,
employee.getWeeklyPlanning(),
employee.getPublicHolidayEventsPlanning());
worksHour = getTotalWeekWorksHours(user, fromDate, toDate, publicHolidays);
workedHour = getTotalWeekWorkedHours(user, fromDate, toDate, publicHolidays);
if (worksHour.compareTo(workedHour) != 0) {
userSet.add(user);
}
} catch (Exception e) {
TraceBackService.trace(e);
}
}
return userSet;
}
@Transactional
public List<Message> sendReminders(TimesheetReport timesheetReport) throws AxelorException {
Template reminderTemplate =
Beans.get(AppHumanResourceService.class).getAppTimesheet().getTimesheetReminderTemplate();
if (reminderTemplate == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_NO_VALUE,
I18n.get(IExceptionMessage.EMPLOYEE_TIMESHEET_REMINDER_TEMPLATE));
}
List<TimesheetReminder> timesheetReminders = getTimesheetReminderList(timesheetReport);
return sendEmailMessage(timesheetReminders, reminderTemplate);
}
private List<TimesheetReminder> getTimesheetReminderList(TimesheetReport timesheetReport) {
List<TimesheetReminder> timesheetReminders = new ArrayList<>();
List<User> users = new ArrayList<>(timesheetReport.getReminderUserSet());
try {
addTimesheetReminder(timesheetReport, users, timesheetReminders);
} catch (Exception e) {
TraceBackService.trace(e);
}
return timesheetReminders;
}
private void addTimesheetReminder(
TimesheetReport timesheetReport, List<User> users, List<TimesheetReminder> timesheetReminders)
throws AxelorException {
BigDecimal worksHour = BigDecimal.ZERO,
workedHour = BigDecimal.ZERO,
missingHour = BigDecimal.ZERO,
extraHour = BigDecimal.ZERO;
LocalDate fromDate = timesheetReport.getFromDate();
LocalDate toDate = null;
do {
toDate = fromDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
if (toDate.until(timesheetReport.getToDate()).getDays() < 0) {
toDate = timesheetReport.getToDate();
}
for (User user : users) {
Employee employee = user.getEmployee();
missingHour = BigDecimal.ZERO;
extraHour = BigDecimal.ZERO;
BigDecimal publicHolidays =
publicHolidayService.computePublicHolidayDays(
fromDate,
toDate,
employee.getWeeklyPlanning(),
employee.getPublicHolidayEventsPlanning());
worksHour = getTotalWeekWorksHours(user, fromDate, toDate, publicHolidays);
workedHour = getTotalWeekWorkedHours(user, fromDate, toDate, publicHolidays);
if (worksHour.compareTo(workedHour) == 1) {
missingHour = worksHour.subtract(workedHour);
} else if (worksHour.compareTo(workedHour) == -1) {
extraHour = workedHour.subtract(worksHour);
}
if (missingHour.compareTo(BigDecimal.ZERO) == 0
&& extraHour.compareTo(BigDecimal.ZERO) == 0) {
continue;
}
Optional<TimesheetReminder> optReminder =
timesheetReminders
.stream()
.filter(reminder -> reminder.getEmployee().getId().compareTo(employee.getId()) == 0)
.findFirst();
TimesheetReminder timesheetReminder = null;
if (optReminder.isPresent()) {
timesheetReminder = optReminder.get();
timesheetReminder.addTimesheetReminderLineListItem(
createTimesheetReminderLine(fromDate, toDate, worksHour, missingHour, extraHour));
} else {
List<TimesheetReminderLine> timesheetReminderLines = new ArrayList<>();
timesheetReminder = new TimesheetReminder();
timesheetReminder.setEmployee(employee);
timesheetReminder.setTimesheetReminderLineList(timesheetReminderLines);
timesheetReminder.addTimesheetReminderLineListItem(
createTimesheetReminderLine(fromDate, toDate, worksHour, missingHour, extraHour));
timesheetReminders.add(timesheetReminder);
}
timesheetReminderRepo.save(timesheetReminder);
}
fromDate = fromDate.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
} while (toDate.until(timesheetReport.getToDate()).getDays() > 0);
}
private List<Message> sendEmailMessage(
List<TimesheetReminder> timesheetReminders, Template reminderTemplate) {
List<Message> messages = new ArrayList<>();
for (TimesheetReminder timesheetReminder : timesheetReminders) {
try {
Message message =
templateMessageService.generateMessage(timesheetReminder, reminderTemplate);
message = messageService.sendMessage(message);
timesheetReminder.setEmailSentDateT(LocalDateTime.now());
messages.add(message);
} catch (Exception e) {
TraceBackService.trace(e);
}
}
return messages;
}
private TimesheetReminderLine createTimesheetReminderLine(
LocalDate fromDate,
LocalDate toDate,
BigDecimal worksHour,
BigDecimal missingHour,
BigDecimal extraHour) {
TimesheetReminderLine line = new TimesheetReminderLine();
line.setFromDate(fromDate);
line.setToDate(toDate);
line.setRequiredHours(worksHour);
line.setExtraHours(extraHour);
line.setMissingHours(missingHour);
line.setWorkHour(worksHour.subtract(missingHour).add(extraHour));
return line;
}
public List<Map<String, Object>> getTimesheetReportList(String TimesheetReportId) {
List<Map<String, Object>> list = new ArrayList<>();
WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 5);
TimesheetReport timesheetReport =
timesheetReportRepository.find(Long.parseLong(TimesheetReportId.toString()));
int numOfDays = timesheetReport.getFromDate().until(timesheetReport.getToDate()).getDays();
List<LocalDate> daysRange =
Stream.iterate(timesheetReport.getFromDate(), date -> date.plusDays(1))
.limit(numOfDays + 1)
.collect(Collectors.toList());
List<User> users = getUsers(timesheetReport);
for (User user : users) {
Employee employee = user.getEmployee();
BigDecimal dailyWorkingHours = employee.getDailyWorkHours();
WeeklyPlanning planning = employee.getWeeklyPlanning();
Integer weekNumber = 1;
int lastDayNumber = -1;
try {
for (LocalDate date : daysRange) {
DayPlanning dayPlanning =
Beans.get(WeeklyPlanningService.class).findDayPlanning(planning, date);
if (dayPlanning == null) {
continue;
}
int dayIndex = date.get(weekFields.dayOfWeek()) - 1;
if (lastDayNumber < dayIndex) {
lastDayNumber = dayIndex;
} else {
lastDayNumber = -1;
weekNumber++;
}
BigDecimal weeklyWorkHours =
employee
.getWeeklyWorkHours()
.multiply(BigDecimal.valueOf((dayIndex) / 6.0))
.setScale(2, RoundingMode.HALF_EVEN);
Map<String, Object> map = getTimesheetMap(user, date, dailyWorkingHours);
map.put("weeklyWorkHours", weeklyWorkHours);
map.put("weekNumber", weekNumber.toString());
list.add(map);
}
} catch (Exception e) {
System.out.println(e);
}
}
return list;
}
private Map<String, Object> getTimesheetMap(
User user, LocalDate date, BigDecimal dailyWorkingHours) throws AxelorException {
Employee employee = user.getEmployee();
BigDecimal worksHour = BigDecimal.ZERO, workedHour = BigDecimal.ZERO;
boolean isPublicHoliday =
publicHolidayService.checkPublicHolidayDay(date, employee.getPublicHolidayEventsPlanning());
worksHour = getTotalWorksHours(user, date, isPublicHoliday, dailyWorkingHours);
try {
workedHour = getTotalWorkedHours(user, date, isPublicHoliday, dailyWorkingHours);
} catch (Exception e) {
System.out.println(e);
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("userName", user.getFullName());
map.put("date", DateTool.toDate(date));
map.put("workedHour", workedHour);
map.put("workingHour", worksHour);
return map;
}
private BigDecimal getTotalWorksHours(
User user, LocalDate date, boolean isPublicHoliday, BigDecimal dailyWorkingHours)
throws AxelorException {
Employee employee = user.getEmployee();
BigDecimal worksHour =
employeeService
.getDaysWorksInPeriod(employee, date, date)
.multiply(employee.getDailyWorkHours());
if (isPublicHoliday) {
worksHour = worksHour.add(dailyWorkingHours);
}
double extraHours =
extraHoursLineRepository
.all()
.filter(
"self.user = ? AND self.date = ? AND (self.extraHours.statusSelect = ? OR self.extraHours.statusSelect = ?)",
user,
date,
ExtraHoursRepository.STATUS_VALIDATED,
ExtraHoursRepository.STATUS_CONFIRMED)
.fetchStream()
.mapToDouble(ehl -> Double.parseDouble(ehl.getQty().toString()))
.sum();
worksHour = worksHour.add(new BigDecimal(extraHours));
return worksHour;
}
private BigDecimal getTotalWeekWorksHours(
User user, LocalDate fromDate, LocalDate toDate, BigDecimal publicHolidays)
throws AxelorException {
Employee employee = user.getEmployee();
BigDecimal worksHour =
employeeService
.getDaysWorksInPeriod(employee, fromDate, toDate)
.multiply(employee.getDailyWorkHours());
worksHour = worksHour.add(publicHolidays.multiply(employee.getDailyWorkHours()));
double extraHours =
extraHoursLineRepository
.all()
.filter(
"self.user = ? AND (self.date BETWEEN ? AND ?) AND (self.extraHours.statusSelect = ? OR self.extraHours.statusSelect = ?)",
user,
fromDate,
toDate,
ExtraHoursRepository.STATUS_VALIDATED,
ExtraHoursRepository.STATUS_CONFIRMED)
.fetchStream()
.mapToDouble(ehl -> Double.parseDouble(ehl.getQty().toString()))
.sum();
worksHour = worksHour.add(new BigDecimal(extraHours));
return worksHour;
}
private BigDecimal getTotalWorkedHours(
User user, LocalDate date, boolean isPublicHoliday, BigDecimal dailyWorkingHours)
throws AxelorException {
BigDecimal totalHours = BigDecimal.ZERO;
List<TimesheetLine> timesheetLineList =
timesheetLineRepository
.all()
.filter(
"self.user = ? AND self.date = ? AND (self.timesheet.statusSelect = ? OR self.timesheet.statusSelect = ?)",
user,
date,
TimesheetRepository.STATUS_CONFIRMED,
TimesheetRepository.STATUS_VALIDATED)
.fetch();
Duration totalDuration = timesheetLineService.computeTotalDuration(timesheetLineList);
totalHours = new BigDecimal(totalDuration.getSeconds()).divide(BigDecimal.valueOf(3600));
if (isPublicHoliday) {
totalHours = totalHours.add(dailyWorkingHours);
} else {
totalHours = totalHours.add(getLeaveHours(user, date, dailyWorkingHours));
}
return totalHours;
}
private BigDecimal getTotalWeekWorkedHours(
User user, LocalDate fromDate, LocalDate toDate, BigDecimal publicHolidays)
throws AxelorException {
BigDecimal totalHours = BigDecimal.ZERO;
Employee employee = user.getEmployee();
List<TimesheetLine> timesheetLineList =
timesheetLineRepository
.all()
.filter(
"self.user = ? AND (self.date BETWEEN ? AND ?) AND (self.timesheet.statusSelect = ? OR self.timesheet.statusSelect = ?)",
user,
fromDate,
toDate,
TimesheetRepository.STATUS_VALIDATED,
TimesheetRepository.STATUS_CONFIRMED)
.fetch();
Duration totalDuration = timesheetLineService.computeTotalDuration(timesheetLineList);
totalHours = new BigDecimal(totalDuration.toHours());
totalHours = totalHours.add(publicHolidays.multiply(employee.getDailyWorkHours()));
totalHours =
totalHours.add(getWeekLeaveHours(user, fromDate, toDate, employee.getDailyWorkHours()));
return totalHours;
}
private BigDecimal getLeaveHours(User user, LocalDate date, BigDecimal dailyWorkingHours)
throws AxelorException {
LeaveRequest leave = leaveService.getLeave(user, date);
if (leave != null) {
return leaveService.computeDuration(leave, date, date).multiply(dailyWorkingHours);
}
return BigDecimal.ZERO;
}
private BigDecimal getWeekLeaveHours(
User user, LocalDate fromDate, LocalDate toDate, BigDecimal dailyWorkingHours)
throws AxelorException {
BigDecimal leaveHours = BigDecimal.ZERO;
do {
LeaveRequest leave = leaveService.getLeave(user, fromDate);
if (leave != null) {
boolean isPublicHoliday =
publicHolidayService.checkPublicHolidayDay(
fromDate, user.getEmployee().getPublicHolidayEventsPlanning());
if (!isPublicHoliday) {
leaveHours =
leaveHours.add(
leaveService
.computeDuration(leave, fromDate, fromDate)
.multiply(dailyWorkingHours));
}
}
fromDate = fromDate.plusDays(1);
} while (fromDate.until(toDate).getDays() > -1);
return leaveHours;
}
protected List<User> getUsers(TimesheetReport timesheetReport) {
QueryBuilder<User> userQuery = QueryBuilder.of(User.class);
userQuery.add(
"self.employee IS NOT NULL AND self.employee.weeklyPlanning IS NOT NULL AND self.employee.publicHolidayEventsPlanning IS NOT NULL AND self.employee.mainEmploymentContract IS NOT NULL");
if (!CollectionUtils.isEmpty(timesheetReport.getUserSet())) {
userQuery.add("self IN :users");
userQuery.bind("users", timesheetReport.getUserSet());
}
return userQuery.build().fetch();
}
}

View File

@ -0,0 +1,167 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.timesheet;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.InvoiceLine;
import com.axelor.apps.base.db.PriceList;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.project.db.Project;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.meta.schema.actions.ActionView;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import javax.mail.MessagingException;
public interface TimesheetService {
@Transactional(rollbackOn = {Exception.class})
public void confirm(Timesheet timesheet) throws AxelorException;
public Message sendConfirmationEmail(Timesheet timesheet)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
public Message confirmAndSendConfirmationEmail(Timesheet timesheet)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
/**
* Checks that there is a line for each working day of the timesheet.
*
* @param timesheet
* @throws AxelorException
*/
public void checkEmptyPeriod(Timesheet timesheet) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void validate(Timesheet timesheet) throws AxelorException;
public Message sendValidationEmail(Timesheet timesheet)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
public Message validateAndSendValidationEmail(Timesheet timesheet)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
@Transactional(rollbackOn = {Exception.class})
public void refuse(Timesheet timesheet) throws AxelorException;
public Message sendRefusalEmail(Timesheet timesheet)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
public Message refuseAndSendRefusalEmail(Timesheet timesheet)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
public void cancel(Timesheet timesheet) throws AxelorException;
/**
* Set the timesheet to draft status.
*
* @param timesheet a timesheet
*/
void draft(Timesheet timesheet);
public Message sendCancellationEmail(Timesheet timesheet)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
public Message cancelAndSendCancellationEmail(Timesheet timesheet)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
public Timesheet generateLines(
Timesheet timesheet,
LocalDate fromGenerationDate,
LocalDate toGenerationDate,
BigDecimal logTime,
Project project,
Product product)
throws AxelorException;
public LocalDate getFromPeriodDate();
public Timesheet getCurrentTimesheet();
public Timesheet getCurrentOrCreateTimesheet();
public Timesheet createTimesheet(User user, LocalDate fromDate, LocalDate toDate);
public List<InvoiceLine> createInvoiceLines(
Invoice invoice, List<TimesheetLine> timesheetLineList, int priority) throws AxelorException;
public List<InvoiceLine> createInvoiceLine(
Invoice invoice,
Product product,
User user,
String date,
BigDecimal hoursDuration,
int priority,
PriceList priceList)
throws AxelorException;
@Transactional
public void computeTimeSpent(Timesheet timesheet);
public BigDecimal computeSubTimeSpent(Project project);
public void computeParentTimeSpent(Project project);
public BigDecimal computeTimeSpent(Project project);
public String computeFullName(Timesheet timesheet);
public List<Map<String, Object>> createDefaultLines(Timesheet timesheet);
public BigDecimal computePeriodTotal(Timesheet timesheet);
public String getPeriodTotalConvertTitle(Timesheet timesheet);
public void createDomainAllTimesheetLine(
User user, Employee employee, ActionView.ActionViewBuilder actionView);
public void createValidateDomainTimesheetLine(
User user, Employee employee, ActionView.ActionViewBuilder actionView);
/**
* Update {@link Timesheet#timeLoggingPreferenceSelect} and recompute all durations.
*
* @param timesheet a context timesheet
* @return the updated timesheet
*/
void updateTimeLoggingPreference(Timesheet timesheet) throws AxelorException;
@Transactional(rollbackOn = {Exception.class})
public void generateLinesFromExpectedProjectPlanning(Timesheet timesheet) throws AxelorException;
public void prefillLines(Timesheet timesheet) throws AxelorException;
public void setTeamTaskTotalRealHrs(List<TimesheetLine> timesheetLines, boolean isAdd);
}

View File

@ -0,0 +1,38 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.timesheet.timer;
import com.axelor.apps.hr.db.TSTimer;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.exception.AxelorException;
import java.math.BigDecimal;
public interface TimesheetTimerService {
public void pause(TSTimer timer);
public void stop(TSTimer timer) throws AxelorException;
public void calculateDuration(TSTimer timer);
public TimesheetLine generateTimesheetLine(TSTimer timer);
public TSTimer getCurrentTSTimer();
public BigDecimal convertSecondDurationInHours(long durationInSeconds);
}

View File

@ -0,0 +1,123 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.timesheet.timer;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.hr.db.TSTimer;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.hr.db.repo.TSTimerRepository;
import com.axelor.apps.hr.db.repo.TimesheetLineRepository;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.timesheet.TimesheetLineService;
import com.axelor.apps.hr.service.timesheet.TimesheetService;
import com.axelor.apps.tool.date.DurationTool;
import com.axelor.auth.AuthUtils;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.inject.persist.Transactional;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TimesheetTimerServiceImpl implements TimesheetTimerService {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Transactional
public void pause(TSTimer timer) {
timer.setStatusSelect(TSTimerRepository.STATUS_PAUSE);
calculateDuration(timer);
}
@Transactional(rollbackOn = {Exception.class})
public void stop(TSTimer timer) throws AxelorException {
timer.setStatusSelect(TSTimerRepository.STATUS_STOP);
calculateDuration(timer);
if (timer.getDuration() > 59) {
generateTimesheetLine(timer);
} else {
throw new AxelorException(
TraceBackRepository.TYPE_FUNCTIONNAL,
I18n.get(IExceptionMessage.NO_TIMESHEET_CREATED),
timer);
}
}
@Transactional
public void calculateDuration(TSTimer timer) {
long currentDuration = timer.getDuration();
Duration duration =
DurationTool.computeDuration(
timer.getTimerStartDateT(),
Beans.get(AppBaseService.class).getTodayDateTime().toLocalDateTime());
long secondes = DurationTool.getSecondsDuration(duration) + currentDuration;
timer.setDuration(secondes);
}
@Transactional
public TimesheetLine generateTimesheetLine(TSTimer timer) {
BigDecimal durationHours = this.convertSecondDurationInHours(timer.getDuration());
Timesheet timesheet = Beans.get(TimesheetService.class).getCurrentOrCreateTimesheet();
LocalDate startDateTime =
(timer.getStartDateTime() == null)
? Beans.get(AppBaseService.class).getTodayDateTime().toLocalDate()
: timer.getStartDateTime().toLocalDate();
TimesheetLine timesheetLine =
Beans.get(TimesheetLineService.class)
.createTimesheetLine(
timer.getProject(),
timer.getProduct(),
timer.getUser(),
startDateTime,
timesheet,
durationHours,
timer.getComments());
Beans.get(TimesheetRepository.class).save(timesheet);
Beans.get(TimesheetLineRepository.class).save(timesheetLine);
timer.setTimesheetLine(timesheetLine);
return timesheetLine;
}
public BigDecimal convertSecondDurationInHours(long durationInSeconds) {
logger.debug("Duration in seconds : {}", durationInSeconds);
BigDecimal durationHours =
new BigDecimal(durationInSeconds).divide(new BigDecimal(3600), 4, RoundingMode.HALF_EVEN);
logger.debug("Duration in hours : {}", durationHours);
return durationHours;
}
public TSTimer getCurrentTSTimer() {
return Beans.get(TSTimerRepository.class)
.all()
.filter("self.user = ?1", AuthUtils.getUser())
.fetchOne();
}
}

View File

@ -0,0 +1,36 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.user;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.auth.db.User;
import com.axelor.meta.CallMethod;
import com.google.inject.persist.Transactional;
public interface UserHrService {
@Transactional
public void createEmployee(User user);
@Transactional
public Company getPayCompany(User user);
@CallMethod
public Product getTimesheetProduct(User user);
}

View File

@ -0,0 +1,108 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.service.user;
import com.axelor.apps.base.db.AppBase;
import com.axelor.apps.base.db.AppLeave;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.EventsPlanning;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.service.app.AppHumanResourceService;
import com.axelor.auth.db.User;
import com.axelor.auth.db.repo.UserRepository;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
public class UserHrServiceImpl implements UserHrService {
@Inject UserRepository userRepo;
@Inject private AppHumanResourceService appHumanResourceService;
@Transactional
public void createEmployee(User user) {
if (user.getPartner() == null) {
Beans.get(UserService.class).createPartner(user);
}
AppBase appBase = appHumanResourceService.getAppBase();
AppLeave appLeave = appHumanResourceService.getAppLeave();
Employee employee = new Employee();
employee.setContactPartner(user.getPartner());
employee.setTimeLoggingPreferenceSelect(appBase.getTimeLoggingPreferenceSelect());
employee.setDailyWorkHours(appBase.getDailyWorkHours());
employee.setNegativeValueLeave(appLeave.getAllowNegativeLeaveEmployees());
EventsPlanning planning = null;
Company company = user.getActiveCompany();
if (company != null) {
HRConfig hrConfig = company.getHrConfig();
if (hrConfig != null) {
planning = hrConfig.getPublicHolidayEventsPlanning();
}
}
employee.setPublicHolidayEventsPlanning(planning);
employee.setUser(user);
Beans.get(EmployeeRepository.class).save(employee);
user.setEmployee(employee);
userRepo.save(user);
}
@Transactional
public Company getPayCompany(User user) {
Company payCompany = null;
if (user.getEmployee() != null
&& user.getEmployee().getMainEmploymentContract() != null
&& user.getEmployee().getMainEmploymentContract().getPayCompany() != null) {
payCompany = user.getEmployee().getMainEmploymentContract().getPayCompany();
} else if (user.getActiveCompany() != null) {
payCompany = user.getActiveCompany();
}
return payCompany;
}
@Override
public Product getTimesheetProduct(User user) {
if (user == null || user.getId() == null || user.getActiveCompany() == null) {
return null;
}
user = userRepo.find(user.getId());
Product product = null;
HRConfig hrConfig = user.getActiveCompany().getHrConfig();
if (hrConfig != null && hrConfig.getUseUniqueProductForTimesheet()) {
product = hrConfig.getUniqueTimesheetProduct();
}
if (product == null && user.getEmployee() != null) {
product = user.getEmployee().getProduct();
}
return product;
}
}

View File

@ -0,0 +1,39 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.translation;
public interface ITranslation {
public static final String EMPLOYEES_MANAGEMENT_APP_NAME = /*$$(*/
"value:Employees Managment"; /*)*/
public static final String EXTRA_HOURS_APP_NAME = /*$$(*/ "value:Extra hours"; /*)*/
public static final String EXPENSE_MANAGEMENT_APP_NAME = /*$$(*/ "value:Expense Management"; /*)*/
public static final String TIMESHEET_MANAGEMENT_APP_NAME = /*$$(*/
"value:Timesheet Management"; /*)*/
public static final String LEAVE_MANAGEMENT_APP_NAME = /*$$(*/ "value:Leave Management"; /*)*/
public static final String PUBLIC_HOLIDAY_TITLE = /*$$(*/ "Public holidays"; /*)*/
public static final String WEEKLY_PLANNING_TITLE = /*$$(*/ "6 days week"; /*)*/
public static final String TS_REPORT_FILL_NO_USER = /*$$(*/ "No user found"; /*)*/
public static final String TS_REPORT_TITLE = /*$$(*/ "TimesheetReport"; /*)*/
public static final String REQUEST_OVERFLOW = /*$$(*/ "Too many requests"; /*)*/
public static final String NO_SUCH_PLACE = /*$$(*/ "No such place exists"; /*)*/
public static final String NO_ROUTE = /*$$(*/ "No Route Found"; /*)*/
}

View File

@ -0,0 +1,154 @@
package com.axelor.apps.hr.web;
import com.axelor.apps.hr.db.Absence;
import com.axelor.apps.hr.db.DailyReport;
import com.axelor.apps.hr.db.Employee;
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.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
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();
// Fetch all existing daily reports associated with this absence
List<DailyReport> existingReports =
Beans.get(DailyReportRepository.class)
.all()
.filter("self.absenceSet = :absence")
.bind("absence", absence)
.fetch();
if (existingReports != null && !existingReports.isEmpty()) {
// Detach absence only from reports that are outside the new date range
List<DailyReport> reportsToDetach =
existingReports
.stream()
.filter(
report ->
report.getReportDate().isBefore(absenceStartDate)
|| report.getReportDate().isAfter(absenceEndDate))
.collect(Collectors.toList());
// Detach absence from these specific reports
if (!reportsToDetach.isEmpty()) {
Beans.get(AbsenceServiceImpl.class)
.deAttachTheAbsenceWithDailyReport(absence, reportsToDetach, false);
}
}
List<DailyReport> newReports =
Beans.get(DailyReportRepository.class)
.all()
.filter(
"self.employee = :employee and self.reportDate between :absenceStartDate and :absenceEndDate")
.bind("employee", employee)
.bind("absenceStartDate", absenceStartDate)
.bind("absenceEndDate", absenceEndDate)
.fetch();
// Check if there are any reports
if (newReports.isEmpty()) {
return;
}
Beans.get(AbsenceServiceImpl.class).attachTheAbsenceWithDailyReport(absence, newReports);
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
Integer selectedType = (Integer) request.getContext().get("selectedType");
// Get the list of selected absence IDs
List<Long> absenceIds = (List<Long>) 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<DailyReport> dailyreports =
Beans.get(DailyReportRepository.class)
.all()
.filter("self.absence = :absence")
.bind("absence", absenceId)
.fetch();
Beans.get(AbsenceServiceImpl.class)
.deAttachTheAbsenceWithDailyReport(absence, dailyreports, true);
ActionViewBuilder actionView =
ActionView.define(I18n.get("Absences"))
.model(Absence.class.getName())
.add("grid", "absence-grid")
.add("form", "absence-form");
response.setView(actionView.map());
} catch (Exception e) {
e.printStackTrace();
}
}
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();
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.web;
import com.axelor.apps.hr.service.app.AppHumanResourceService;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
@Singleton
public class AppHumanResourceController {
public void generateHrConfigurations(ActionRequest request, ActionResponse response) {
Beans.get(AppHumanResourceService.class).generateHrConfigurations();
response.setReload(true);
}
}

View File

@ -0,0 +1,373 @@
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 void fetchSalaryAuthorization(ActionRequest request, ActionResponse response) {
try {
String accessToken = AppBaseController.getAccessToken();
if (accessToken == null) {
logger.error("Access token is null, unable to proceed.");
return;
}
String jsonResponse = fetchAP(accessToken);
if (jsonResponse != null) {
try {
JSONArray jsonArray = new JSONArray(jsonResponse);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
Beans.get(AuthorizationService.class).saveAP(jsonObject);
}
// Reload the response after successfully saving extra hours
response.setReload(true);
} catch (JSONException e) {
// Log the specific JSON parsing error
System.err.println("Failed to parse JSON: " + jsonResponse);
e.printStackTrace();
}
} else {
System.err.println("No response received from fetchAP.");
}
} catch (Exception e) {
// General catch for unexpected exceptions
System.err.println(
"An error occurred while fetching Salary Authorization: " + e.getMessage());
e.printStackTrace();
}
}
public String fetchAA(String accessToken) {
String endpoint = BASE_URL + "tickets/aa/";
HttpURLConnection conn = null;
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
}
}
}
// Authorisation de paye
public String fetchAP(String accessToken) {
String endpoint = BASE_URL + "tickets/ap/";
HttpURLConnection conn = null;
try {
URL url = new URL(endpoint);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept", "application/json");
// Include the access token in the Authorization header
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
try (BufferedReader br =
new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
// Process the response as needed
return response.toString();
}
} else {
// Read the error stream for more details
StringBuilder errorResponse = new StringBuilder();
try (BufferedReader br =
new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"))) {
String errorLine;
while ((errorLine = br.readLine()) != null) {
errorResponse.append(errorLine.trim());
}
}
throw new RuntimeException(
"Failed: HTTP error code: " + responseCode + ", Error: " + errorResponse.toString());
}
} catch (Exception e) {
e.printStackTrace();
return null; // Return null in case of failure
} finally {
if (conn != null) {
conn.disconnect(); // Ensure connection is closed
}
}
}
}

View File

@ -0,0 +1,233 @@
package com.axelor.apps.hr.web;
import com.axelor.apps.hr.db.DailyReport;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.service.DailyReportService;
import com.axelor.apps.hr.service.DailyReportServiceImpl;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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<DailyReport> 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<Long> dailyReportIds = (List<Long>) request.getContext().get("_ids");
if (dailyReportIds != null && !dailyReportIds.isEmpty()) {
List<DailyReport> 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<DailyReport> 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.");
}
public void massDeducePrime(ActionRequest request, ActionResponse response) {
Integer primeSelection = (Integer) request.getContext().get("primesSelect");
String startDateStr = (String) request.getContext().get("startDate");
String endDateStr = (String) request.getContext().get("endDate");
LocalDate startDate = null;
LocalDate endDate = null;
try {
if (startDateStr != null) {
startDate = LocalDate.parse(startDateStr);
}
if (endDateStr != null) {
endDate = LocalDate.parse(endDateStr);
}
// Validate the dates
if (startDate == null || endDate == null) {
response.setFlash("Start date or end date is missing or invalid.");
return;
}
if (startDate.isAfter(endDate)) {
response.setFlash("Start date cannot be after end date.");
return;
}
} catch (DateTimeParseException e) {
response.setFlash(
"Invalid date format for startDate or endDate. Expected format: yyyy-MM-dd.");
return;
}
Object valueToDeduceObj = request.getContext().get("valueToDeduce");
BigDecimal valueToDeduce = null;
if (valueToDeduceObj instanceof Integer) {
valueToDeduce = BigDecimal.valueOf((Integer) valueToDeduceObj);
} else if (valueToDeduceObj instanceof BigDecimal) {
valueToDeduce = (BigDecimal) valueToDeduceObj;
} else if (valueToDeduceObj instanceof String) {
try {
valueToDeduce = new BigDecimal((String) valueToDeduceObj);
} catch (NumberFormatException e) {
response.setFlash("Value to deduce must be a valid number.");
return;
}
} else {
response.setFlash(
"Invalid value to deduce: unsupported type "
+ (valueToDeduceObj != null ? valueToDeduceObj.getClass().getName() : "null"));
return;
}
Object employeesObject = request.getContext().get("employees");
// Initialize the employees list
List<Employee> employees = new ArrayList<>();
if (employeesObject instanceof List) {
List<LinkedHashMap<String, Object>> employeesList =
(List<LinkedHashMap<String, Object>>) employeesObject;
for (LinkedHashMap<String, Object> employeeMap : employeesList) {
Integer employeeIdInt = (Integer) employeeMap.get("id");
if (employeeIdInt != null) {
Long employeeId = employeeIdInt.longValue();
Employee employee = Beans.get(EmployeeRepository.class).find(employeeId);
if (employee != null) {
employees.add(employee);
}
}
}
}
if (employees.isEmpty()) {
response.setFlash("No employees selected.");
return;
}
List<Integer> employeeIds = new ArrayList<>();
for (Employee employee : employees) {
employeeIds.add(employee.getId().intValue());
}
// Fetch all rapport journaliers within the date range for all employees
List<DailyReport> dailyReportList =
Beans.get(DailyReportRepository.class)
.all()
.filter(
"self.employee.id in :employeeIds and self.reportDate >= :startDate and self.reportDate <= :endDate")
.bind("startDate", startDate)
.bind("endDate", endDate)
.bind("employeeIds", employeeIds)
.fetch();
try {
if (!dailyReportList.isEmpty()) {
Beans.get(DailyReportService.class)
.massDeducePrimes(dailyReportList, primeSelection, valueToDeduce);
response.setReload(true);
response.setFlash("Prime deductions processed successfully.");
} else {
response.setFlash("No reports found for the selected date range.");
}
} catch (Exception e) {
TraceBackService.trace(response, e);
response.setFlash("An error occurred while processing the request.");
}
}
}

View File

@ -0,0 +1,72 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.web;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.service.PeriodService;
import com.axelor.apps.hr.db.EmployeeBonusMgt;
import com.axelor.apps.hr.db.repo.EmployeeBonusMgtRepository;
import com.axelor.apps.hr.report.IReport;
import com.axelor.apps.hr.service.EmployeeBonusService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
@Singleton
public class EmployeeBonusController {
public void compute(ActionRequest request, ActionResponse response) {
EmployeeBonusMgt employeeBonusMgt = request.getContext().asType(EmployeeBonusMgt.class);
PeriodService periodService = Beans.get(PeriodService.class);
try {
employeeBonusMgt = Beans.get(EmployeeBonusMgtRepository.class).find(employeeBonusMgt.getId());
Beans.get(EmployeeBonusService.class).compute(employeeBonusMgt);
response.setReload(true);
periodService.checkPeriod(employeeBonusMgt.getPayPeriod());
periodService.checkPeriod(employeeBonusMgt.getLeavePeriod());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void print(ActionRequest request, ActionResponse response) throws AxelorException {
EmployeeBonusMgt bonus =
Beans.get(EmployeeBonusMgtRepository.class)
.find(request.getContext().asType(EmployeeBonusMgt.class).getId());
String name =
I18n.get("Employee bonus management") + " : " + bonus.getEmployeeBonusType().getLabel();
String fileLink =
ReportFactory.createReport(IReport.EMPLOYEE_BONUS_MANAGEMENT, name)
.addParam("EmployeeBonusMgtId", bonus.getId())
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.toAttach(bonus)
.generate()
.getFileLink();
response.setView(ActionView.define(name).add("html", fileLink).map());
}
}

View File

@ -0,0 +1,284 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.web;
import com.axelor.app.AppSettings;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.hr.db.DPAE;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.Granding;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.GrandingRepository;
import com.axelor.apps.hr.report.IReport;
import com.axelor.apps.hr.service.employee.EmployeeService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.schema.actions.ActionView;
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.IOException;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wslite.json.JSONException;
import wslite.json.JSONObject;
@Singleton
public class EmployeeController {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final String pythonScriptDir = AppSettings.get().get("services.dir");
public void showAnnualReport(ActionRequest request, ActionResponse response)
throws JSONException, NumberFormatException, AxelorException {
String employeeId = request.getContext().get("_id").toString();
String year = request.getContext().get("year").toString();
int yearId = new JSONObject(year).getInt("id");
String yearName = new JSONObject(year).getString("name");
User user = AuthUtils.getUser();
String name =
I18n.get("Annual expenses report") + " : " + user.getFullName() + " (" + yearName + ")";
String fileLink =
ReportFactory.createReport(IReport.EMPLOYEE_ANNUAL_REPORT, name)
.addParam("EmployeeId", Long.valueOf(employeeId))
.addParam("YearId", Long.valueOf(yearId))
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.toAttach(Beans.get(EmployeeRepository.class).find(Long.valueOf(employeeId)))
.generate()
.getFileLink();
response.setView(ActionView.define(name).add("html", fileLink).map());
response.setCanClose(true);
}
public void setEmployeeSocialNetworkUrl(ActionRequest request, ActionResponse response) {
Employee employee = request.getContext().asType(Employee.class);
if (employee.getContactPartner() != null) {
Map<String, String> urlMap =
Beans.get(EmployeeService.class)
.getSocialNetworkUrl(
employee.getContactPartner().getName(),
employee.getContactPartner().getFirstName());
response.setAttr("contactPartner.facebookLabel", "title", urlMap.get("facebook"));
response.setAttr("contactPartner.twitterLabel", "title", urlMap.get("twitter"));
response.setAttr("contactPartner.linkedinLabel", "title", urlMap.get("linkedin"));
response.setAttr("contactPartner.youtubeLabel", "title", urlMap.get("youtube"));
}
}
public void setContactSocialNetworkUrl(ActionRequest request, ActionResponse response) {
Partner partnerContact = request.getContext().asType(Partner.class);
Map<String, String> urlMap =
Beans.get(EmployeeService.class)
.getSocialNetworkUrl(partnerContact.getName(), partnerContact.getFirstName());
response.setAttr("facebookLabel", "title", urlMap.get("facebook"));
response.setAttr("twitterLabel", "title", urlMap.get("twitter"));
response.setAttr("linkedinLabel", "title", urlMap.get("linkedin"));
response.setAttr("youtubeLabel", "title", urlMap.get("youtube"));
}
public void printEmployeePhonebook(ActionRequest request, ActionResponse response)
throws AxelorException {
User user = AuthUtils.getUser();
String name = I18n.get("Employee PhoneBook");
String fileLink =
ReportFactory.createReport(IReport.EMPLOYEE_PHONEBOOK, name + "-${date}")
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addParam("UserId", user.getId())
.generate()
.getFileLink();
LOG.debug("Printing " + name);
response.setView(ActionView.define(name).add("html", fileLink).map());
}
public void generateNewDPAE(ActionRequest request, ActionResponse response) {
Employee employee = request.getContext().asType(Employee.class);
employee = Beans.get(EmployeeRepository.class).find(employee.getId());
try {
Long dpaeId = Beans.get(EmployeeService.class).generateNewDPAE(employee);
ActionViewBuilder builder =
ActionView.define(I18n.get("DPAE"))
.model(DPAE.class.getName())
.add("grid", "dpae-grid")
.add("form", "dpae-form")
.context("_showRecord", dpaeId);
response.setView(builder.map());
} catch (AxelorException e) {
TraceBackService.trace(response, e);
}
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();
if (pythonScriptDir == null || pythonScriptDir.isEmpty()) {
LOG.error("Pythons script path is not configured in AppSettings.");
return;
}
try {
String[] args = {
"python",
pythonScriptDir + "\\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).");
Beans.get(EmployeeService.class).setEmployeeEnrolled(employee);
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();
}
}
}
}
public void setEmployeeConfig(ActionRequest request, ActionResponse response) {
List<Long> employeeIds = (List<Long>) request.getContext().get("_ids");
System.out.println("****************");
System.out.println(employeeIds);
System.out.println("****************");
Integer configSelect = (Integer) request.getContext().get("configSelect");
if (employeeIds == null || employeeIds.isEmpty()) {
response.setFlash("You haven't selected any Employee.");
}
try {
if (!employeeIds.isEmpty()) {
Beans.get(EmployeeService.class).massUpdateEmployeeConfig(employeeIds, configSelect, true);
response.setReload(true);
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void unSetEmployeeConfig(ActionRequest request, ActionResponse response) {
List<Long> employeeIds = (List<Long>) request.getContext().get("_ids");
System.out.println("****************");
System.out.println(employeeIds);
System.out.println("****************");
Integer configSelect = (Integer) request.getContext().get("configSelect");
if (employeeIds == null || employeeIds.isEmpty()) {
response.setFlash("You haven't selected any Employee.");
}
try {
if (!employeeIds.isEmpty()) {
Beans.get(EmployeeService.class).massUpdateEmployeeConfig(employeeIds, configSelect, false);
response.setReload(true);
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.web;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.repo.EmploymentContractRepository;
import com.axelor.apps.hr.service.EmploymentContractService;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
import java.io.IOException;
@Singleton
public class EmploymentContractController {
public void addAmendment(ActionRequest request, ActionResponse response) {
EmploymentContract employmentContract = request.getContext().asType(EmploymentContract.class);
try {
Beans.get(EmploymentContractService.class)
.addAmendment(
Beans.get(EmploymentContractRepository.class).find(employmentContract.getId()));
response.setFlash(
String.format(
"Contrat %s - avenant %s",
employmentContract.getFullName(), employmentContract.getEmploymentContractVersion()));
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void exportEmploymentContract(ActionRequest request, ActionResponse response)
throws IOException {
EmploymentContract employmentContract =
Beans.get(EmploymentContractRepository.class)
.find(request.getContext().asType(EmploymentContract.class).getId());
Beans.get(EmploymentContractService.class).exportEmploymentContract(employmentContract);
response.setReload(true);
}
}

View File

@ -0,0 +1,84 @@
package com.axelor.apps.hr.web;
import com.axelor.apps.hr.db.Granding;
import com.axelor.apps.hr.db.repo.GrandingRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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();
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.web;
import com.axelor.apps.base.db.Batch;
import com.axelor.apps.hr.db.HrBatch;
import com.axelor.apps.hr.db.repo.HrBatchRepository;
import com.axelor.apps.hr.service.batch.HrBatchService;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
@Singleton
public class HrBatchController {
/**
* Launch any type of HR batch
*
* @param request
* @param response
* @throws AxelorException
*/
public void launchHrBatch(ActionRequest request, ActionResponse response) throws AxelorException {
HrBatch hrBatch = request.getContext().asType(HrBatch.class);
Batch batch =
Beans.get(HrBatchService.class)
.run(Beans.get(HrBatchRepository.class).find(hrBatch.getId()));
if (batch != null) {
response.setFlash(batch.getComments());
}
response.setReload(true);
}
}

View File

@ -0,0 +1,281 @@
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.DailyReport;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.MonthlyReport;
import com.axelor.apps.hr.db.repo.AbsenceRepository;
import com.axelor.apps.hr.db.repo.DailyReportRepository;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.MonthlyReportRepository;
import com.axelor.apps.hr.service.MonthlyReportServiceImpl;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import java.lang.invoke.MethodHandles;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MonthlyReportController {
private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public void createMensuelReport(ActionRequest request, ActionResponse response) {
try {
Object object = request.getContext().get("period");
LinkedHashMap<String, Object> periodMap = (LinkedHashMap<String, Object>) 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<Employee> 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<DailyReport> 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<Absence> 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<DailyReport> employeeDailyReports =
allDailyReports
.stream()
.filter(r -> r.getEmployee().equals(employee))
.collect(Collectors.toList());
// Filter absences for the current employee
List<Absence> employeeAbsences =
allAbsences
.stream()
.filter(a -> a.getEmployee().equals(employee))
.collect(Collectors.toList());
if (!employeeDailyReports.isEmpty() || !employeeAbsences.isEmpty()) {
System.out.println(
"Create monthly report for employee: " + employee.getRegistrationNumber());
// Process the employee's monthly report using filtered rapport and absences
Beans.get(MonthlyReportServiceImpl.class)
.createMensuelReport(
employee, period, startDate, endDate, employeeDailyReports, employeeAbsences);
} else {
log.error("No Daily Reports exist for employee: " + employee.getRegistrationNumber());
}
}
// Indicate that the action was successful and a reload is needed
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<String, Object> periodMap = (LinkedHashMap<String, Object>) 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<Employee> employees = new ArrayList<>();
// Check if employeesObject is not null and cast it to a list
if (employeesObject instanceof List) {
List<LinkedHashMap<String, Object>> employeesList =
(List<LinkedHashMap<String, Object>>) employeesObject;
// Loop through each employee in the list
for (LinkedHashMap<String, Object> 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<Integer> employeesIds = new ArrayList<>();
for (Employee e : employees) {
employeesIds.add(e.getId().intValue());
}
// Fetch all rapport journaliers within the date range for all employees
List<DailyReport> 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<Absence> 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();
for (Employee employee : employees) {
// Check if a MonthlyReport exists for this employee in the specified period
MonthlyReport monthlyReport =
Beans.get(MonthlyReportRepository.class)
.all()
.filter("self.employee = :employee and self.period = :period")
.bind("employee", employee)
.bind("period", period)
.fetchOne();
Optional<MonthlyReport> monthlyReportOpt = Optional.ofNullable(monthlyReport);
// Filter daily reports for the current employee
List<DailyReport> employeeDailyReports =
allDailyReports
.stream()
.filter(r -> r.getEmployee().equals(employee))
.collect(Collectors.toList());
// Filter absences for the current employee
List<Absence> employeeAbsences =
allAbsences
.stream()
.filter(a -> a.getEmployee().equals(employee))
.collect(Collectors.toList());
if (!employeeDailyReports.isEmpty() || !employeeAbsences.isEmpty()) {
if (monthlyReportOpt.isPresent()) {
MonthlyReport existingReport = monthlyReportOpt.get();
System.out.println(
"Update monthly report for employee: " + employee.getRegistrationNumber());
// Update the existing monthly report
Beans.get(MonthlyReportServiceImpl.class)
.updateMensuelReport(
existingReport,
employee,
period,
startDate,
endDate,
employeeDailyReports,
employeeAbsences);
} else {
System.out.println(
"Create monthly report for employee: " + employee.getRegistrationNumber());
// Create a new monthly report
Beans.get(MonthlyReportServiceImpl.class)
.createMensuelReport(
employee, period, startDate, endDate, employeeDailyReports, employeeAbsences);
}
} else {
System.err.println(
"No Daily Reports exist for employee: " + employee.getRegistrationNumber());
}
}
// Indicate that the action was successful and a reload is needed
response.setReload(true);
} catch (Exception e) {
// 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;
}
}

View File

@ -0,0 +1,103 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.web;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.PayrollLeave;
import com.axelor.apps.hr.db.PayrollPreparation;
import com.axelor.apps.hr.db.repo.EmploymentContractRepository;
import com.axelor.apps.hr.db.repo.HrBatchRepository;
import com.axelor.apps.hr.db.repo.PayrollPreparationRepository;
import com.axelor.apps.hr.service.PayrollPreparationService;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.List;
@Singleton
public class PayrollPreparationController {
public void generateFromEmploymentContract(ActionRequest request, ActionResponse response) {
PayrollPreparation payrollPreparation = request.getContext().asType(PayrollPreparation.class);
EmploymentContract employmentContract =
Beans.get(EmploymentContractRepository.class)
.find(new Long(request.getContext().get("_idEmploymentContract").toString()));
response.setValues(
Beans.get(PayrollPreparationService.class)
.generateFromEmploymentContract(payrollPreparation, employmentContract));
}
public void fillInPayrollPreparation(ActionRequest request, ActionResponse response)
throws AxelorException {
PayrollPreparation payrollPreparation = request.getContext().asType(PayrollPreparation.class);
List<PayrollLeave> payrollLeaveList =
Beans.get(PayrollPreparationService.class).fillInPayrollPreparation(payrollPreparation);
response.setValue("extraHoursLineList", payrollPreparation.getExtraHoursLineList());
response.setValue("$payrollLeavesList", payrollLeaveList);
response.setValue("duration", payrollPreparation.getDuration());
response.setValue("leaveDuration", payrollPreparation.getLeaveDuration());
response.setValue("expenseAmount", payrollPreparation.getExpenseAmount());
response.setValue("expenseList", payrollPreparation.getExpenseList());
response.setValue(
"otherCostsEmployeeSet",
payrollPreparation.getEmploymentContract().getOtherCostsEmployeeSet());
response.setValue(
"annualGrossSalary", payrollPreparation.getEmploymentContract().getAnnualGrossSalary());
response.setValue("employeeBonusMgtLineList", payrollPreparation.getEmployeeBonusMgtLineList());
response.setValue("lunchVoucherNumber", payrollPreparation.getLunchVoucherNumber());
response.setValue("lunchVoucherMgtLineList", payrollPreparation.getLunchVoucherMgtLineList());
}
public void fillInPayrollPreparationLeaves(ActionRequest request, ActionResponse response)
throws AxelorException {
PayrollPreparation payrollPreparation = request.getContext().asType(PayrollPreparation.class);
List<PayrollLeave> payrollLeaveList =
Beans.get(PayrollPreparationService.class).fillInLeaves(payrollPreparation);
response.setValue("$payrollLeavesList", payrollLeaveList);
}
public void exportPayrollPreparation(ActionRequest request, ActionResponse response)
throws IOException, AxelorException {
PayrollPreparationService payrollPreparationService =
Beans.get(PayrollPreparationService.class);
PayrollPreparation payrollPreparation =
Beans.get(PayrollPreparationRepository.class)
.find(request.getContext().asType(PayrollPreparation.class).getId());
if (payrollPreparation.getExportTypeSelect() == HrBatchRepository.EXPORT_TYPE_STANDARD) {
response.setExportFile(
payrollPreparationService.exportSinglePayrollPreparation(payrollPreparation));
} else if (payrollPreparation.getExportTypeSelect() == HrBatchRepository.EXPORT_TYPE_NIBELIS) {
response.setExportFile(
payrollPreparationService.exportNibelisPayrollPreparation(payrollPreparation));
}
payrollPreparationService.closePayPeriodIfExported(payrollPreparation);
response.setReload(true);
}
}

View File

@ -0,0 +1,95 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.web;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.EmploymentContract;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.service.user.UserHrService;
import com.axelor.auth.db.User;
import com.axelor.auth.db.repo.UserRepository;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.axelor.rpc.Context;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.util.List;
@Singleton
public class UserHrController {
@Transactional
public void createEmployee(ActionRequest request, ActionResponse response) {
User user =
Beans.get(UserRepository.class).find(request.getContext().asType(User.class).getId());
Beans.get(UserHrService.class).createEmployee(user);
response.setReload(true);
}
@Transactional
public void createUser(ActionRequest request, ActionResponse response) {
Context context = request.getContext();
User user = context.asType(User.class);
EmployeeRepository employeeRepository = Beans.get(EmployeeRepository.class);
User employeeUser = new User();
employeeUser.setActivateOn(user.getActivateOn());
employeeUser.setExpiresOn(user.getExpiresOn());
employeeUser.setCode(user.getCode());
employeeUser.setGroup(user.getGroup());
if (context.containsKey("_id")) {
Object employeeId = context.get("_id");
if (employeeId != null) {
Employee employee = employeeRepository.find(Long.parseLong(employeeId.toString()));
employeeUser.setEmployee(employeeRepository.find(employee.getId()));
if (employee.getContactPartner() != null) {
String employeeName = employee.getContactPartner().getName();
if (employee.getContactPartner().getFirstName() != null) {
employeeName += " " + employee.getContactPartner().getFirstName();
}
employeeUser.setName(employeeName);
if (employee.getContactPartner().getEmailAddress() != null) {
employeeUser.setEmail(employee.getContactPartner().getEmailAddress().getAddress());
}
}
if (employee.getMainEmploymentContract() != null) {
employeeUser.setActiveCompany(employee.getMainEmploymentContract().getPayCompany());
}
List<EmploymentContract> contractList = employee.getEmploymentContractList();
if (contractList != null && !contractList.isEmpty()) {
for (EmploymentContract employmentContract : contractList) {
employeeUser.addCompanySetItem(employmentContract.getPayCompany());
}
}
CharSequence password = Beans.get(UserService.class).generateRandomPassword();
employeeUser.setPassword(password.toString());
employee.setUser(employeeUser);
}
}
Beans.get(UserRepository.class).save(employeeUser);
response.setCanClose(true);
}
}

View File

@ -0,0 +1,694 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
< * Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.axelor.apps.hr.web.expense;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.account.db.Move;
import com.axelor.apps.account.service.app.AppAccountService;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Wizard;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.base.service.message.MessageServiceBaseImpl;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.ExpenseLine;
import com.axelor.apps.hr.db.ExtraHours;
import com.axelor.apps.hr.db.KilometricAllowParam;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.report.IReport;
import com.axelor.apps.hr.service.HRMenuTagService;
import com.axelor.apps.hr.service.HRMenuValidateService;
import com.axelor.apps.hr.service.KilometricService;
import com.axelor.apps.hr.service.app.AppHumanResourceService;
import com.axelor.apps.hr.service.expense.ExpenseService;
import com.axelor.apps.hr.service.user.UserHrService;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.db.repo.MessageRepository;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.apps.tool.StringTool;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.db.JPA;
import com.axelor.db.Query;
import com.axelor.exception.AxelorException;
import com.axelor.exception.ResponseMessageType;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.meta.schema.actions.ActionView.ActionViewBuilder;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.axelor.rpc.Context;
import com.google.common.base.Strings;
import com.google.inject.Singleton;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @author axelor */
@Singleton
public class ExpenseController {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public void createAnalyticDistributionWithTemplate(ActionRequest request, ActionResponse response)
throws AxelorException {
ExpenseLine expenseLine = request.getContext().asType(ExpenseLine.class);
expenseLine =
Beans.get(ExpenseService.class).createAnalyticDistributionWithTemplate(expenseLine);
response.setValue("analyticMoveLineList", expenseLine.getAnalyticMoveLineList());
}
public void computeAnalyticDistribution(ActionRequest request, ActionResponse response)
throws AxelorException {
ExpenseLine expenseLine = request.getContext().asType(ExpenseLine.class);
Expense expense = expenseLine.getExpense();
if (expense == null) {
setExpense(request, expenseLine);
}
if (Beans.get(AppAccountService.class).getAppAccount().getManageAnalyticAccounting()) {
expenseLine = Beans.get(ExpenseService.class).computeAnalyticDistribution(expenseLine);
response.setValue("analyticMoveLineList", expenseLine.getAnalyticMoveLineList());
}
}
public void editExpense(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Company activeCompany = user.getActiveCompany();
List<Expense> expenseList =
Beans.get(ExpenseRepository.class)
.all()
.filter(
"self.user = ?1 AND self.company = ?2 AND self.statusSelect = 1 AND (self.multipleUsers is false OR self.multipleUsers is null)",
user,
activeCompany)
.fetch();
if (expenseList.isEmpty()) {
response.setView(
ActionView.define(I18n.get("Expense"))
.model(Expense.class.getName())
.add("form", "expense-form")
.context("_payCompany", Beans.get(UserHrService.class).getPayCompany(user))
.map());
} else if (expenseList.size() == 1) {
response.setView(
ActionView.define(I18n.get("Expense"))
.model(Expense.class.getName())
.add("form", "expense-form")
.param("forceEdit", "true")
.context("_showRecord", String.valueOf(expenseList.get(0).getId()))
.map());
} else {
response.setView(
ActionView.define(I18n.get("Expense"))
.model(Wizard.class.getName())
.add("form", "popup-expense-form")
.param("forceEdit", "true")
.param("popup", "true")
.param("show-toolbar", "false")
.param("show-confirm", "false")
.param("forceEdit", "true")
.param("popup-save", "false")
.map());
}
}
@SuppressWarnings("unchecked")
public void editExpenseSelected(ActionRequest request, ActionResponse response) {
Map<String, Object> expenseMap =
(Map<String, Object>) request.getContext().get("expenseSelect");
if (expenseMap == null) {
response.setError(I18n.get(IExceptionMessage.EXPENSE_NOT_SELECTED));
return;
}
Long expenseId = Long.valueOf((Integer) expenseMap.get("id"));
response.setCanClose(true);
response.setView(
ActionView.define(I18n.get("Expense"))
.model(Expense.class.getName())
.add("form", "expense-form")
.param("forceEdit", "true")
.domain("self.id = " + expenseId)
.context("_showRecord", expenseId)
.map());
}
public void validateExpense(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Expenses to Validate"))
.model(Expense.class.getName())
.add("grid", "expense-validate-grid")
.add("form", "expense-form");
Beans.get(HRMenuValidateService.class).createValidateDomain(user, employee, actionView);
response.setView(actionView.map());
}
public void historicExpense(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Historic colleague Expenses"))
.model(Expense.class.getName())
.add("grid", "expense-grid")
.add("form", "expense-form");
actionView
.domain(
"self.company = :_activeCompany AND (self.statusSelect = 3 OR self.statusSelect = 4)")
.context("_activeCompany", user.getActiveCompany());
if (employee == null || !employee.getHrManager()) {
actionView
.domain(actionView.get().getDomain() + " AND self.user.employee.managerUser = :_user")
.context("_user", user);
}
response.setView(actionView.map());
}
public void showSubordinateExpenses(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Company activeCompany = user.getActiveCompany();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Expenses to be Validated by your subordinates"))
.model(Expense.class.getName())
.add("grid", "expense-grid")
.add("form", "expense-form");
String domain =
"self.user.employee.managerUser.employee.managerUser = :_user AND self.company = :_activeCompany AND self.statusSelect = 2";
long nbExpenses =
Query.of(ExtraHours.class)
.filter(domain)
.bind("_user", user)
.bind("_activeCompany", activeCompany)
.count();
if (nbExpenses == 0) {
response.setNotify(I18n.get("No expense to be validated by your subordinates"));
} else {
response.setView(
actionView
.domain(domain)
.context("_user", user)
.context("_activeCompany", activeCompany)
.map());
}
}
/**
* Called from expense form, on expense lines change. Call {@link ExpenseService#compute(Expense)}
*
* @param request
* @param response
*/
public void compute(ActionRequest request, ActionResponse response) {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseService.class).compute(expense);
response.setValues(expense);
}
public void updateMoveDateAndPeriod(ActionRequest request, ActionResponse response) {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseService.class).updateMoveDateAndPeriod(expense);
response.setValue("moveDate", expense.getMoveDate());
response.setValue("period", expense.getPeriod());
}
public void ventilate(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseRepository.class).find(expense.getId());
Move move = Beans.get(ExpenseService.class).ventilate(expense);
response.setReload(true);
if (move != null) {
response.setView(
ActionView.define(I18n.get("Move"))
.model(Move.class.getName())
.add("grid", "move-grid")
.add("form", "move-form")
.context("_showRecord", String.valueOf(move.getId()))
.map());
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void printExpense(ActionRequest request, ActionResponse response) throws AxelorException {
Expense expense = request.getContext().asType(Expense.class);
String name = I18n.get("Expense") + " " + expense.getFullName().replace("/", "-");
String fileLink =
ReportFactory.createReport(IReport.EXPENSE, name)
.addParam("ExpenseId", expense.getId())
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.toAttach(expense)
.generate()
.getFileLink();
logger.debug("Printing {}", name);
response.setView(ActionView.define(name).add("html", fileLink).map());
}
/* Count Tags displayed on the menu items */
public String expenseValidateMenuTag() {
return Beans.get(HRMenuTagService.class)
.countRecordsTag(Expense.class, ExpenseRepository.STATUS_CONFIRMED);
}
public String expenseVentilateMenuTag() {
Long total =
JPA.all(Expense.class).filter("self.statusSelect = 3 AND self.ventilated = false").count();
return String.format("%s", total);
}
public void cancel(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseRepository.class).find(expense.getId());
ExpenseService expenseService = Beans.get(ExpenseService.class);
expenseService.cancel(expense);
Message message = expenseService.sendCancellationEmail(expense);
if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
response.setFlash(
String.format(
I18n.get("Email sent to %s"),
Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
} finally {
response.setReload(true);
}
}
public void addPayment(ActionRequest request, ActionResponse response) {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseRepository.class).find(expense.getId());
try {
Beans.get(ExpenseService.class).addPayment(expense);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called on clicking cancelPaymentButton, call {@link ExpenseService#cancelPayment(Expense)}.
*
* @param request
* @param response
*/
public void cancelPayment(ActionRequest request, ActionResponse response) {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseRepository.class).find(expense.getId());
try {
Beans.get(ExpenseService.class).cancelPayment(expense);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
// sending expense and sending mail to manager
public void send(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseRepository.class).find(expense.getId());
ExpenseService expenseService = Beans.get(ExpenseService.class);
expenseService.confirm(expense);
Message message = expenseService.sendConfirmationEmail(expense);
if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
response.setFlash(
String.format(
I18n.get("Email sent to %s"),
Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
} finally {
response.setReload(true);
}
}
public void newExpense(ActionResponse response) {
response.setView(
ActionView.define(I18n.get("Expense"))
.model(Expense.class.getName())
.add("form", "expense-form")
.context(
"_payCompany", Beans.get(UserHrService.class).getPayCompany(AuthUtils.getUser()))
.map());
}
// validating expense and sending mail to applicant
public void valid(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseRepository.class).find(expense.getId());
ExpenseService expenseService = Beans.get(ExpenseService.class);
expenseService.validate(expense);
Message message = expenseService.sendValidationEmail(expense);
if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
response.setFlash(
String.format(
I18n.get("Email sent to %s"),
Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
} finally {
response.setReload(true);
}
}
// refusing expense and sending mail to applicant
public void refuse(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Expense expense = request.getContext().asType(Expense.class);
expense = Beans.get(ExpenseRepository.class).find(expense.getId());
ExpenseService expenseService = Beans.get(ExpenseService.class);
expenseService.refuse(expense);
Message message = expenseService.sendRefusalEmail(expense);
if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
response.setFlash(
String.format(
I18n.get("Email sent to %s"),
Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
} finally {
response.setReload(true);
}
}
public void fillKilometricExpenseProduct(ActionRequest request, ActionResponse response)
throws AxelorException {
try {
Expense expense = request.getContext().getParent().asType(Expense.class);
Product expenseProduct = Beans.get(ExpenseService.class).getKilometricExpenseProduct(expense);
logger.debug("Get Kilometric expense product : {}", expenseProduct);
response.setValue("expenseProduct", expenseProduct);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void validateAndCompute(ActionRequest request, ActionResponse response) {
Expense expense = request.getContext().asType(Expense.class);
ExpenseService expenseService = Beans.get(ExpenseService.class);
List<Integer> expenseLineListId = new ArrayList<>();
int compt = 0;
for (ExpenseLine expenseLine : expenseService.getExpenseLineList(expense)) {
compt++;
if (expenseLine.getExpenseDate().isAfter(Beans.get(AppBaseService.class).getTodayDate())) {
expenseLineListId.add(compt);
}
}
try {
if (!expenseLineListId.isEmpty()) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get("Date can't be in the future for line(s) : %s"),
expenseLineListId.stream().map(id -> id.toString()).collect(Collectors.joining(",")));
}
} catch (AxelorException e) {
TraceBackService.trace(response, e, ResponseMessageType.ERROR);
}
response.setValue(
"personalExpenseAmount", expenseService.computePersonalExpenseAmount(expense));
response.setValue("advanceAmount", expenseService.computeAdvanceAmount(expense));
if (expense.getKilometricExpenseLineList() != null
&& !expense.getKilometricExpenseLineList().isEmpty()) {
response.setValue("kilometricExpenseLineList", expense.getKilometricExpenseLineList());
}
compute(request, response);
}
public void computeKilometricExpense(ActionRequest request, ActionResponse response)
throws AxelorException {
ExpenseLine expenseLine = request.getContext().asType(ExpenseLine.class);
if (expenseLine.getKilometricAllowParam() == null
|| expenseLine.getDistance().compareTo(BigDecimal.ZERO) == 0
|| expenseLine.getExpenseDate() == null) {
return;
}
String userId;
String userName;
if (expenseLine.getExpense() != null) {
setExpense(request, expenseLine);
}
Expense expense = expenseLine.getExpense();
if (expense != null && expenseLine.getUser() != null) {
userId = expense.getUser().getId().toString();
userName = expense.getUser().getFullName();
} else {
userId = request.getContext().getParent().asType(Expense.class).getUser().getId().toString();
userName = request.getContext().getParent().asType(Expense.class).getUser().getFullName();
}
Employee employee =
Beans.get(EmployeeRepository.class).all().filter("self.user.id = ?1", userId).fetchOne();
if (employee == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
userName);
}
BigDecimal amount = BigDecimal.ZERO;
try {
amount = Beans.get(KilometricService.class).computeKilometricExpense(expenseLine, employee);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
response.setValue("totalAmount", amount);
response.setValue("untaxedAmount", amount);
}
public void updateKAPOfKilometricAllowance(ActionRequest request, ActionResponse response)
throws AxelorException {
ExpenseLine expenseLine = request.getContext().asType(ExpenseLine.class);
if (expenseLine.getExpense() == null) {
setExpense(request, expenseLine);
}
try {
List<KilometricAllowParam> kilometricAllowParamList =
Beans.get(ExpenseService.class).getListOfKilometricAllowParamVehicleFilter(expenseLine);
if (kilometricAllowParamList == null || kilometricAllowParamList.isEmpty()) {
response.setAttr("kilometricAllowParam", "domain", "self.id IN (0)");
} else {
response.setAttr(
"kilometricAllowParam",
"domain",
"self.id IN (" + StringTool.getIdListString(kilometricAllowParamList) + ")");
}
KilometricAllowParam currentKilometricAllowParam = expenseLine.getKilometricAllowParam();
boolean vehicleOk = false;
if (kilometricAllowParamList != null && kilometricAllowParamList.size() == 1) {
response.setValue("kilometricAllowParam", kilometricAllowParamList.get(0));
} else if (kilometricAllowParamList != null) {
for (KilometricAllowParam kilometricAllowParam : kilometricAllowParamList) {
if (currentKilometricAllowParam != null
&& currentKilometricAllowParam.equals(kilometricAllowParam)) {
expenseLine.setKilometricAllowParam(kilometricAllowParam);
vehicleOk = true;
break;
}
}
if (!vehicleOk) {
response.setValue("kilometricAllowParam", null);
} else {
response.setValue("kilometricAllowParam", expenseLine.getKilometricAllowParam());
}
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
private void setExpense(ActionRequest request, ExpenseLine expenseLine) {
Context parent = request.getContext().getParent();
if (parent != null && parent.get("_model").equals(Expense.class.getName())) {
expenseLine.setExpense(parent.asType(Expense.class));
}
}
public void domainOnSelectOnKAP(ActionRequest request, ActionResponse response)
throws AxelorException {
ExpenseLine expenseLine = request.getContext().asType(ExpenseLine.class);
if (expenseLine.getExpense() == null) {
setExpense(request, expenseLine);
}
try {
List<KilometricAllowParam> kilometricAllowParamList =
Beans.get(ExpenseService.class).getListOfKilometricAllowParamVehicleFilter(expenseLine);
response.setAttr(
"kilometricAllowParam",
"domain",
"self.id IN (" + StringTool.getIdListString(kilometricAllowParamList) + ")");
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void computeDistanceAndKilometricExpense(ActionRequest request, ActionResponse response)
throws AxelorException {
// Compute distance.
try {
if (!Beans.get(AppHumanResourceService.class)
.getAppExpense()
.getComputeDistanceWithWebService()) {
return;
}
Context context = request.getContext();
ExpenseLine expenseLine = context.asType(ExpenseLine.class);
if (Strings.isNullOrEmpty(expenseLine.getFromCity())
|| Strings.isNullOrEmpty(expenseLine.getToCity())) {
return;
}
KilometricService kilometricService = Beans.get(KilometricService.class);
BigDecimal distance = kilometricService.computeDistance(expenseLine);
expenseLine.setDistance(distance);
response.setValue("distance", distance);
// Compute kilometric expense.
if (expenseLine.getKilometricAllowParam() == null
|| expenseLine.getExpenseDate() == null
|| expenseLine.getKilometricTypeSelect() == 0) {
return;
}
Expense expense = expenseLine.getExpense();
if (expense == null) {
expense = context.getParent().asType(Expense.class);
}
Employee employee = expense.getUser().getEmployee();
if (employee == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
expense.getUser().getName());
}
BigDecimal amount = kilometricService.computeKilometricExpense(expenseLine, employee);
response.setValue("totalAmount", amount);
response.setValue("untaxedAmount", amount);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

Some files were not shown because too many files have changed in this diff Show More