initial commit

This commit is contained in:
devapp
2021-11-29 11:56:30 +01:00
parent 016e45a01c
commit 5b67068faa
4285 changed files with 927085 additions and 2 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,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,211 @@
/*
* 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" /*)*/;
}

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

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,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,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,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.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;
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;
}

View File

@@ -0,0 +1,253 @@
/*
* 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();
}
}

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,58 @@
/*
* 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;
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;
public Message sendRefusalEmail(ExtraHours extraHours)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException;
public void compute(ExtraHours extraHours);
}

View File

@@ -0,0 +1,165 @@
/*
* 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.service.app.AppBaseService;
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.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.util.List;
import javax.mail.MessagingException;
public class ExtraHoursServiceImpl implements ExtraHoursService {
protected ExtraHoursRepository extraHoursRepo;
protected AppBaseService appBaseService;
protected HRConfigService hrConfigService;
protected TemplateMessageService templateMessageService;
@Inject
public ExtraHoursServiceImpl(
ExtraHoursRepository extraHoursRepo,
AppBaseService appBaseService,
HRConfigService hrConfigService,
TemplateMessageService templateMessageService) {
this.extraHoursRepo = extraHoursRepo;
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);
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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;
public interface LeaveService {
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,916 @@
/*
* 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.Company;
import com.axelor.apps.base.db.DayPlanning;
import com.axelor.apps.base.db.EventsPlanning;
import com.axelor.apps.base.db.ICalendarEvent;
import com.axelor.apps.base.db.WeeklyPlanning;
import com.axelor.apps.base.db.repo.ICalendarEventRepository;
import com.axelor.apps.base.ical.ICalendarService;
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.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.repo.LeaveLineRepository;
import com.axelor.apps.hr.db.repo.LeaveReasonRepository;
import com.axelor.apps.hr.db.repo.LeaveRequestRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.hr.service.publicHoliday.PublicHolidayHrService;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.service.TemplateMessageService;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.common.ObjectUtils;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
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.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import javax.mail.MessagingException;
public class LeaveServiceImpl implements LeaveService {
protected LeaveLineRepository leaveLineRepo;
protected WeeklyPlanningService weeklyPlanningService;
protected PublicHolidayHrService publicHolidayHrService;
protected LeaveRequestRepository leaveRequestRepo;
protected AppBaseService appBaseService;
protected HRConfigService hrConfigService;
protected TemplateMessageService templateMessageService;
protected ICalendarEventRepository icalEventRepo;
protected ICalendarService icalendarService;
@Inject
public LeaveServiceImpl(
LeaveLineRepository leaveLineRepo,
WeeklyPlanningService weeklyPlanningService,
PublicHolidayHrService publicHolidayHrService,
LeaveRequestRepository leaveRequestRepo,
AppBaseService appBaseService,
HRConfigService hrConfigService,
TemplateMessageService templateMessageService,
ICalendarEventRepository icalEventRepo,
ICalendarService icalendarService) {
this.leaveLineRepo = leaveLineRepo;
this.weeklyPlanningService = weeklyPlanningService;
this.publicHolidayHrService = publicHolidayHrService;
this.leaveRequestRepo = leaveRequestRepo;
this.appBaseService = appBaseService;
this.hrConfigService = hrConfigService;
this.templateMessageService = templateMessageService;
this.icalEventRepo = icalEventRepo;
this.icalendarService = icalendarService;
}
/**
* Compute the duration of a given leave request but restricted inside a period.
*
* @param leave
* @param fromDate the first date of the period
* @param toDate the last date of the period
* @return the computed duration in days
* @throws AxelorException
*/
public BigDecimal computeDuration(LeaveRequest leave, LocalDate fromDate, LocalDate toDate)
throws AxelorException {
LocalDateTime leaveFromDate = leave.getFromDateT();
LocalDateTime leaveToDate = leave.getToDateT();
int startOn = leave.getStartOnSelect();
int endOn = leave.getEndOnSelect();
LocalDateTime from = leaveFromDate;
LocalDateTime to = leaveToDate;
// if the leave starts before the beginning of the period,
// we use the beginning date of the period.
if (leaveFromDate.toLocalDate().isBefore(fromDate)) {
from = fromDate.atStartOfDay();
startOn = LeaveRequestRepository.SELECT_MORNING;
}
// if the leave ends before the end of the period,
// we use the last date of the period.
if (leaveToDate.toLocalDate().isAfter(toDate)) {
to = toDate.atStartOfDay();
endOn = LeaveRequestRepository.SELECT_AFTERNOON;
}
return computeDuration(leave, from, to, startOn, endOn);
}
/**
* Compute the duration of a given leave request.
*
* @param leave
* @return the computed duration in days
* @throws AxelorException
*/
public BigDecimal computeDuration(LeaveRequest leave) throws AxelorException {
return computeDuration(
leave,
leave.getFromDateT(),
leave.getToDateT(),
leave.getStartOnSelect(),
leave.getEndOnSelect());
}
/**
* Compute the duration of a given leave request. The duration can be in hours or in days,
* depending of the leave reason of the leave.
*
* @param leave
* @param from, the beginning of the leave request inside the period
* @param to, the end of the leave request inside the period
* @param startOn If the period starts in the morning or in the afternoon
* @param endOn If the period ends in the morning or in the afternoon
* @return the computed duration in days
* @throws AxelorException
*/
public BigDecimal computeDuration(
LeaveRequest leave, LocalDateTime from, LocalDateTime to, int startOn, int endOn)
throws AxelorException {
BigDecimal duration = BigDecimal.ZERO;
if (from != null
&& to != null
&& leave.getLeaveLine() != null
&& leave.getLeaveLine().getLeaveReason() != null) {
Employee employee = leave.getUser().getEmployee();
if (employee == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
leave.getUser().getName());
}
switch (leave.getLeaveLine().getLeaveReason().getUnitSelect()) {
case LeaveReasonRepository.UNIT_SELECT_DAYS:
LocalDate fromDate = from.toLocalDate();
LocalDate toDate = to.toLocalDate();
duration = computeDurationInDays(leave, employee, fromDate, toDate, startOn, endOn);
break;
case LeaveReasonRepository.UNIT_SELECT_HOURS:
duration = computeDurationInHours(leave, employee, from, to);
break;
default:
throw new AxelorException(
leave.getLeaveLine().getLeaveReason(),
TraceBackRepository.CATEGORY_NO_VALUE,
I18n.get(IExceptionMessage.LEAVE_REASON_NO_UNIT),
leave.getLeaveLine().getLeaveReason().getLeaveReason());
}
}
return duration.signum() != -1 ? duration : BigDecimal.ZERO;
}
/**
* Computes the duration in days of a leave, according to the input planning.
*
* @param leave
* @param employee
* @param fromDate
* @param toDate
* @param startOn
* @param endOn
* @return
* @throws AxelorException
*/
protected BigDecimal computeDurationInDays(
LeaveRequest leave,
Employee employee,
LocalDate fromDate,
LocalDate toDate,
int startOn,
int endOn)
throws AxelorException {
BigDecimal duration = BigDecimal.ZERO;
WeeklyPlanning weeklyPlanning = getWeeklyPlanning(leave, employee);
EventsPlanning holidayPlanning = getPublicHolidayEventsPlanning(leave, employee);
// If the leave request is only for 1 day
if (fromDate.isEqual(toDate)) {
if (startOn == endOn) {
if (startOn == LeaveRequestRepository.SELECT_MORNING) {
duration =
duration.add(
BigDecimal.valueOf(
weeklyPlanningService.getWorkingDayValueInDaysWithSelect(
weeklyPlanning, fromDate, true, false)));
} else {
duration =
duration.add(
BigDecimal.valueOf(
weeklyPlanningService.getWorkingDayValueInDaysWithSelect(
weeklyPlanning, fromDate, false, true)));
}
} else {
duration =
duration.add(
BigDecimal.valueOf(
weeklyPlanningService.getWorkingDayValueInDaysWithSelect(
weeklyPlanning, fromDate, true, true)));
}
// Else if it's on several days
} else {
duration =
duration.add(
BigDecimal.valueOf(computeStartDateWithSelect(fromDate, startOn, weeklyPlanning)));
LocalDate itDate = fromDate.plusDays(1);
while (!itDate.isEqual(toDate) && !itDate.isAfter(toDate)) {
duration =
duration.add(
BigDecimal.valueOf(
weeklyPlanningService.getWorkingDayValueInDays(weeklyPlanning, itDate)));
itDate = itDate.plusDays(1);
}
duration =
duration.add(BigDecimal.valueOf(computeEndDateWithSelect(toDate, endOn, weeklyPlanning)));
}
if (holidayPlanning != null) {
duration =
duration.subtract(
publicHolidayHrService.computePublicHolidayDays(
fromDate, toDate, weeklyPlanning, holidayPlanning));
}
return duration;
}
/**
* Computes the duration in hours of a leave, according to the weekly and the holiday plannings.
*
* @param weeklyPlanning
* @param holidayPlanning
* @param fromDateT
* @param toDateT
* @return
* @throws AxelorException
*/
protected BigDecimal computeDurationInHours(
LeaveRequest leave, Employee employee, LocalDateTime fromDateT, LocalDateTime toDateT)
throws AxelorException {
BigDecimal duration = BigDecimal.ZERO;
WeeklyPlanning weeklyPlanning = getWeeklyPlanning(leave, employee);
EventsPlanning holidayPlanning = getPublicHolidayEventsPlanning(leave, employee);
LocalDate fromDate = fromDateT.toLocalDate();
LocalDate toDate = toDateT.toLocalDate();
if (toDate.equals(fromDate)
&& !publicHolidayHrService.checkPublicHolidayDay(fromDate, holidayPlanning)) {
duration =
duration.add(
weeklyPlanningService.getWorkingDayValueInHours(
weeklyPlanning, fromDate, fromDateT.toLocalTime(), toDateT.toLocalTime()));
} else {
// First day of leave
if (!publicHolidayHrService.checkPublicHolidayDay(fromDate, holidayPlanning)) {
duration =
duration.add(
weeklyPlanningService.getWorkingDayValueInHours(
weeklyPlanning, fromDate, fromDateT.toLocalTime(), null));
}
fromDate = fromDate.plusDays(1);
// Last day of leave
if (!publicHolidayHrService.checkPublicHolidayDay(toDate, holidayPlanning)) {
duration =
duration.add(
weeklyPlanningService.getWorkingDayValueInHours(
weeklyPlanning, toDate, null, toDateT.toLocalTime()));
}
// Daily leave duration of the other days between from and to date
for (LocalDate date = fromDate; date.isBefore(toDate); date = date.plusDays(1)) {
if (!publicHolidayHrService.checkPublicHolidayDay(date, holidayPlanning)) {
duration =
duration.add(
weeklyPlanningService.getWorkingDayValueInHours(
weeklyPlanning, date, null, null));
}
}
}
return duration;
}
protected WeeklyPlanning getWeeklyPlanning(LeaveRequest leave, Employee employee)
throws AxelorException {
WeeklyPlanning weeklyPlanning = employee.getWeeklyPlanning();
if (weeklyPlanning == null) {
Company comp = leave.getCompany();
if (comp != null) {
HRConfig conf = comp.getHrConfig();
if (conf != null) {
weeklyPlanning = conf.getWeeklyPlanning();
}
}
}
if (weeklyPlanning == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EMPLOYEE_PLANNING),
employee.getName());
}
return weeklyPlanning;
}
protected EventsPlanning getPublicHolidayEventsPlanning(LeaveRequest leave, Employee employee) {
EventsPlanning publicHolidayPlanning = employee.getPublicHolidayEventsPlanning();
if (publicHolidayPlanning == null
&& leave.getCompany() != null
&& leave.getCompany().getHrConfig() != null) {
publicHolidayPlanning = leave.getCompany().getHrConfig().getPublicHolidayEventsPlanning();
}
return publicHolidayPlanning;
}
@Transactional(rollbackOn = {Exception.class})
public void manageSentLeaves(LeaveRequest leave) throws AxelorException {
Employee employee = leave.getUser().getEmployee();
if (employee == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
leave.getUser().getName());
}
LeaveLine leaveLine =
leaveLineRepo
.all()
.filter(
"self.employee = ?1 AND self.leaveReason = ?2",
employee,
leave.getLeaveLine().getLeaveReason())
.fetchOne();
if (leaveLine == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_LINE),
employee.getName(),
leave.getLeaveLine().getLeaveReason().getLeaveReason());
}
if (leave.getInjectConsumeSelect() == LeaveRequestRepository.SELECT_CONSUME) {
leaveLine.setDaysToValidate(leaveLine.getDaysToValidate().subtract(leave.getDuration()));
} else {
leaveLine.setDaysToValidate(leaveLine.getDaysToValidate().add(leave.getDuration()));
}
}
@Transactional(rollbackOn = {Exception.class})
public void manageValidateLeaves(LeaveRequest leave) throws AxelorException {
Employee employee = leave.getUser().getEmployee();
if (employee == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
leave.getUser().getName());
}
LeaveLine leaveLine =
leaveLineRepo
.all()
.filter(
"self.employee = ?1 AND self.leaveReason = ?2",
employee,
leave.getLeaveLine().getLeaveReason())
.fetchOne();
if (leaveLine == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_LINE),
employee.getName(),
leave.getLeaveLine().getLeaveReason().getLeaveReason());
}
if (leave.getInjectConsumeSelect() == LeaveRequestRepository.SELECT_CONSUME) {
leaveLine.setQuantity(leaveLine.getQuantity().subtract(leave.getDuration()));
if (leaveLine.getQuantity().signum() == -1 && !employee.getNegativeValueLeave()) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_ALLOW_NEGATIVE_VALUE_EMPLOYEE),
employee.getName());
}
if (leaveLine.getQuantity().signum() == -1
&& !leave.getLeaveLine().getLeaveReason().getAllowNegativeValue()) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_ALLOW_NEGATIVE_VALUE_REASON),
leave.getLeaveLine().getLeaveReason().getLeaveReason());
}
leaveLine.setDaysToValidate(leaveLine.getDaysToValidate().add(leave.getDuration()));
leaveLine.setDaysValidated(leaveLine.getDaysValidated().add(leave.getDuration()));
} else {
leaveLine.setQuantity(leaveLine.getQuantity().add(leave.getDuration()));
leaveLine.setDaysToValidate(leaveLine.getDaysToValidate().subtract(leave.getDuration()));
}
}
@Transactional(rollbackOn = {Exception.class})
public void manageRefuseLeaves(LeaveRequest leave) throws AxelorException {
Employee employee = leave.getUser().getEmployee();
if (employee == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
leave.getUser().getName());
}
LeaveLine leaveLine =
leaveLineRepo
.all()
.filter(
"self.employee = ?1 AND self.leaveReason = ?2",
employee,
leave.getLeaveLine().getLeaveReason())
.fetchOne();
if (leaveLine == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_LINE),
employee.getName(),
leave.getLeaveLine().getLeaveReason().getLeaveReason());
}
if (leave.getInjectConsumeSelect() == LeaveRequestRepository.SELECT_CONSUME) {
leaveLine.setDaysToValidate(leaveLine.getDaysToValidate().add(leave.getDuration()));
} else {
leaveLine.setDaysToValidate(leaveLine.getDaysToValidate().subtract(leave.getDuration()));
}
}
@Transactional(rollbackOn = {Exception.class})
public void manageCancelLeaves(LeaveRequest leave) throws AxelorException {
Employee employee = leave.getUser().getEmployee();
if (employee == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
leave.getUser().getName());
}
LeaveLine leaveLine =
leaveLineRepo
.all()
.filter(
"self.employee = ?1 AND self.leaveReason = ?2",
employee,
leave.getLeaveLine().getLeaveReason())
.fetchOne();
if (leaveLine == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_LINE),
employee.getName(),
leave.getLeaveLine().getLeaveReason().getLeaveReason());
}
if (leave.getStatusSelect() == LeaveRequestRepository.STATUS_VALIDATED) {
if (leave.getInjectConsumeSelect() == LeaveRequestRepository.SELECT_CONSUME) {
leaveLine.setQuantity(leaveLine.getQuantity().add(leave.getDuration()));
} else {
leaveLine.setQuantity(leaveLine.getQuantity().subtract(leave.getDuration()));
}
leaveLine.setDaysValidated(leaveLine.getDaysValidated().subtract(leave.getDuration()));
} else if (leave.getStatusSelect() == LeaveRequestRepository.STATUS_AWAITING_VALIDATION) {
if (leave.getInjectConsumeSelect() == LeaveRequestRepository.SELECT_CONSUME) {
leaveLine.setDaysToValidate(leaveLine.getDaysToValidate().add(leave.getDuration()));
} else {
leaveLine.setDaysToValidate(leaveLine.getDaysToValidate().subtract(leave.getDuration()));
}
}
}
public double computeStartDateWithSelect(
LocalDate date, int select, WeeklyPlanning weeklyPlanning) {
double value = 0;
if (select == LeaveRequestRepository.SELECT_MORNING) {
value = weeklyPlanningService.getWorkingDayValueInDays(weeklyPlanning, date);
} else {
DayPlanning dayPlanning = weeklyPlanningService.findDayPlanning(weeklyPlanning, date);
if (dayPlanning != null
&& dayPlanning.getAfternoonFrom() != null
&& dayPlanning.getAfternoonTo() != null) {
value = 0.5;
}
}
return value;
}
public double computeEndDateWithSelect(
LocalDate date, int select, WeeklyPlanning weeklyPlanning) {
double value = 0;
if (select == LeaveRequestRepository.SELECT_AFTERNOON) {
value = weeklyPlanningService.getWorkingDayValueInDays(weeklyPlanning, date);
} else {
DayPlanning dayPlanning = weeklyPlanningService.findDayPlanning(weeklyPlanning, date);
if (dayPlanning != null
&& dayPlanning.getMorningFrom() != null
&& dayPlanning.getMorningTo() != null) {
value = 0.5;
}
}
return value;
}
@Transactional(rollbackOn = {Exception.class})
public LeaveRequest createEvents(LeaveRequest leave) throws AxelorException {
Employee employee = leave.getUser().getEmployee();
if (employee == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE),
leave.getUser().getName());
}
WeeklyPlanning weeklyPlanning = employee.getWeeklyPlanning();
if (weeklyPlanning == null) {
throw new AxelorException(
leave,
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessage.EMPLOYEE_PLANNING),
employee.getName());
}
LocalDateTime fromDateTime;
LocalDateTime toDateTime;
if (leave.getLeaveLine().getLeaveReason().getUnitSelect()
== LeaveReasonRepository.UNIT_SELECT_DAYS) {
fromDateTime = getDefaultStart(weeklyPlanning, leave);
toDateTime = getDefaultEnd(weeklyPlanning, leave);
} else {
fromDateTime = leave.getFromDateT();
toDateTime = leave.getToDateT();
}
ICalendarEvent event =
icalendarService.createEvent(
fromDateTime,
toDateTime,
leave.getUser(),
leave.getComments(),
4,
leave.getLeaveLine().getLeaveReason().getLeaveReason()
+ " "
+ leave.getUser().getFullName());
icalEventRepo.save(event);
leave.setIcalendarEvent(event);
return leave;
}
protected LocalDateTime getDefaultStart(WeeklyPlanning weeklyPlanning, LeaveRequest leave) {
int startTimeHour = 0;
int startTimeMin = 0;
DayPlanning startDay =
weeklyPlanningService.findDayPlanning(weeklyPlanning, leave.getFromDateT().toLocalDate());
if (leave.getStartOnSelect() == LeaveRequestRepository.SELECT_MORNING) {
if (startDay != null && startDay.getMorningFrom() != null) {
startTimeHour = startDay.getMorningFrom().getHour();
startTimeMin = startDay.getMorningFrom().getMinute();
} else {
startTimeHour = 8;
startTimeMin = 0;
}
} else {
if (startDay != null && startDay.getAfternoonFrom() != null) {
startTimeHour = startDay.getAfternoonFrom().getHour();
startTimeMin = startDay.getAfternoonFrom().getMinute();
} else {
startTimeHour = 14;
startTimeMin = 0;
}
}
return LocalDateTime.of(
leave.getFromDateT().toLocalDate(), LocalTime.of(startTimeHour, startTimeMin));
}
protected LocalDateTime getDefaultEnd(WeeklyPlanning weeklyPlanning, LeaveRequest leave) {
int endTimeHour = 0;
int endTimeMin = 0;
DayPlanning endDay =
weeklyPlanningService.findDayPlanning(weeklyPlanning, leave.getToDateT().toLocalDate());
if (leave.getEndOnSelect() == LeaveRequestRepository.SELECT_MORNING) {
if (endDay != null && endDay.getMorningTo() != null) {
endTimeHour = endDay.getMorningTo().getHour();
endTimeMin = endDay.getMorningTo().getMinute();
} else {
endTimeHour = 12;
endTimeMin = 0;
}
} else {
if (endDay != null && endDay.getAfternoonTo() != null) {
endTimeHour = endDay.getAfternoonTo().getHour();
endTimeMin = endDay.getAfternoonTo().getMinute();
} else {
endTimeHour = 18;
endTimeMin = 0;
}
}
return LocalDateTime.of(
leave.getToDateT().toLocalDate(), LocalTime.of(endTimeHour, endTimeMin));
}
public BigDecimal computeLeaveDaysByLeaveRequest(
LocalDate fromDate, LocalDate toDate, LeaveRequest leaveRequest, Employee employee)
throws AxelorException {
BigDecimal leaveDays = BigDecimal.ZERO;
WeeklyPlanning weeklyPlanning = employee.getWeeklyPlanning();
LocalDate leaveFrom = leaveRequest.getFromDateT().toLocalDate();
LocalDate leaveTo = leaveRequest.getToDateT().toLocalDate();
LocalDate itDate = fromDate;
if (fromDate.isBefore(leaveFrom) || fromDate.equals(leaveFrom)) {
itDate = leaveFrom;
}
boolean morningHalf = false;
boolean eveningHalf = false;
BigDecimal daysToAdd = BigDecimal.ZERO;
if (leaveTo.equals(leaveFrom)
&& leaveRequest.getStartOnSelect() == leaveRequest.getEndOnSelect()) {
eveningHalf = leaveRequest.getStartOnSelect() == LeaveRequestRepository.SELECT_AFTERNOON;
morningHalf = leaveRequest.getStartOnSelect() == LeaveRequestRepository.SELECT_MORNING;
}
while (!itDate.isEqual(leaveTo.plusDays(1)) && !itDate.isEqual(toDate.plusDays(1))) {
if (itDate.equals(leaveFrom) && !morningHalf) {
daysToAdd =
BigDecimal.valueOf(
computeStartDateWithSelect(
itDate, leaveRequest.getStartOnSelect(), weeklyPlanning));
} else if (itDate.equals(leaveTo) && !eveningHalf) {
daysToAdd =
BigDecimal.valueOf(
computeEndDateWithSelect(itDate, leaveRequest.getEndOnSelect(), weeklyPlanning));
} else {
daysToAdd =
BigDecimal.valueOf(
weeklyPlanningService.getWorkingDayValueInDays(weeklyPlanning, itDate));
}
if (!publicHolidayHrService.checkPublicHolidayDay(itDate, employee)) {
leaveDays = leaveDays.add(daysToAdd);
}
itDate = itDate.plusDays(1);
}
return leaveDays;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void cancel(LeaveRequest leaveRequest) throws AxelorException {
if (leaveRequest.getLeaveLine().getLeaveReason().getManageAccumulation()) {
manageCancelLeaves(leaveRequest);
}
if (leaveRequest.getIcalendarEvent() != null) {
ICalendarEvent event = leaveRequest.getIcalendarEvent();
leaveRequest.setIcalendarEvent(null);
icalEventRepo.remove(icalEventRepo.find(event.getId()));
}
leaveRequest.setStatusSelect(LeaveRequestRepository.STATUS_CANCELED);
}
public Message sendCancellationEmail(LeaveRequest leaveRequest)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException {
HRConfig hrConfig = hrConfigService.getHRConfig(leaveRequest.getCompany());
if (hrConfig.getLeaveMailNotification()) {
return templateMessageService.generateAndSendMessage(
leaveRequest, hrConfigService.getCanceledLeaveTemplate(hrConfig));
}
return null;
}
@Override
@Transactional(rollbackOn = {Exception.class})
public void confirm(LeaveRequest leaveRequest) throws AxelorException {
if (leaveRequest.getLeaveLine().getLeaveReason().getManageAccumulation()) {
manageSentLeaves(leaveRequest);
}
leaveRequest.setStatusSelect(LeaveRequestRepository.STATUS_AWAITING_VALIDATION);
leaveRequest.setRequestDate(appBaseService.getTodayDate());
leaveRequestRepo.save(leaveRequest);
}
public Message sendConfirmationEmail(LeaveRequest leaveRequest)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException {
HRConfig hrConfig = hrConfigService.getHRConfig(leaveRequest.getCompany());
if (hrConfig.getLeaveMailNotification()) {
return templateMessageService.generateAndSendMessage(
leaveRequest, hrConfigService.getSentLeaveTemplate(hrConfig));
}
return null;
}
@Transactional(rollbackOn = {Exception.class})
public void validate(LeaveRequest leaveRequest) throws AxelorException {
LeaveLine leaveLine = leaveRequest.getLeaveLine();
if (leaveLine.getLeaveReason().getManageAccumulation()) {
manageValidateLeaves(leaveRequest);
}
leaveRequest.setStatusSelect(LeaveRequestRepository.STATUS_VALIDATED);
leaveRequest.setValidatedBy(AuthUtils.getUser());
leaveRequest.setValidationDate(appBaseService.getTodayDate());
leaveRequest.setQuantityBeforeValidation(leaveLine.getQuantity());
leaveRequestRepo.save(leaveRequest);
createEvents(leaveRequest);
}
public Message sendValidationEmail(LeaveRequest leaveRequest)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException {
HRConfig hrConfig = hrConfigService.getHRConfig(leaveRequest.getCompany());
if (hrConfig.getLeaveMailNotification()) {
return templateMessageService.generateAndSendMessage(
leaveRequest, hrConfigService.getValidatedLeaveTemplate(hrConfig));
}
return null;
}
@Transactional(rollbackOn = {Exception.class})
public void refuse(LeaveRequest leaveRequest) throws AxelorException {
if (leaveRequest.getLeaveLine().getLeaveReason().getManageAccumulation()) {
manageRefuseLeaves(leaveRequest);
}
leaveRequest.setStatusSelect(LeaveRequestRepository.STATUS_REFUSED);
leaveRequest.setRefusedBy(AuthUtils.getUser());
leaveRequest.setRefusalDate(appBaseService.getTodayDate());
leaveRequestRepo.save(leaveRequest);
}
public Message sendRefusalEmail(LeaveRequest leaveRequest)
throws AxelorException, ClassNotFoundException, InstantiationException,
IllegalAccessException, MessagingException, IOException {
HRConfig hrConfig = hrConfigService.getHRConfig(leaveRequest.getCompany());
if (hrConfig.getLeaveMailNotification()) {
return templateMessageService.generateAndSendMessage(
leaveRequest, hrConfigService.getRefusedLeaveTemplate(hrConfig));
}
return null;
}
public boolean willHaveEnoughDays(LeaveRequest leaveRequest) {
LocalDateTime todayDate = appBaseService.getTodayDateTime().toLocalDateTime();
LocalDateTime beginDate = leaveRequest.getFromDateT();
int interval =
(beginDate.getYear() - todayDate.getYear()) * 12
+ beginDate.getMonthValue()
- todayDate.getMonthValue();
BigDecimal num =
leaveRequest
.getLeaveLine()
.getQuantity()
.add(
leaveRequest
.getUser()
.getEmployee()
.getWeeklyPlanning()
.getLeaveCoef()
.multiply(
leaveRequest.getLeaveLine().getLeaveReason().getDefaultDayNumberGain())
.multiply(BigDecimal.valueOf(interval)));
return leaveRequest.getDuration().compareTo(num) <= 0;
}
@Transactional
public LeaveLine getLeaveReasonToJustify(Employee employee, LeaveReason leaveReason) {
LeaveLine leaveLineBase = null;
if ((employee.getLeaveLineList() != null) || (!employee.getLeaveLineList().isEmpty())) {
for (LeaveLine leaveLine : employee.getLeaveLineList()) {
if (leaveReason.equals(leaveLine.getLeaveReason())) {
leaveLineBase = leaveLine;
}
}
}
return leaveLineBase;
}
@Transactional(rollbackOn = {Exception.class})
public LeaveLine createLeaveReasonToJustify(Employee employee, LeaveReason leaveReason)
throws AxelorException {
LeaveLine leaveLineEmployee = new LeaveLine();
leaveLineEmployee.setLeaveReason(leaveReason);
leaveLineEmployee.setEmployee(employee);
leaveLineRepo.save(leaveLineEmployee);
return leaveLineEmployee;
}
@Transactional(rollbackOn = {Exception.class})
public LeaveLine addLeaveReasonOrCreateIt(Employee employee, LeaveReason leaveReason)
throws AxelorException {
LeaveLine leaveLine = getLeaveReasonToJustify(employee, leaveReason);
if ((leaveLine == null) || (leaveLine.getLeaveReason() != leaveReason)) {
leaveLine = createLeaveReasonToJustify(employee, leaveReason);
}
return leaveLine;
}
public boolean isLeaveDay(User user, LocalDate date) {
return getLeave(user, date) != null;
}
public LeaveRequest getLeave(User user, LocalDate date) {
List<LeaveRequest> leaves =
JPA.all(LeaveRequest.class)
.filter("self.user = :userId AND self.statusSelect IN (:awaitingValidation,:validated)")
.bind("userId", user)
.bind("awaitingValidation", LeaveRequestRepository.STATUS_AWAITING_VALIDATION)
.bind("validated", LeaveRequestRepository.STATUS_VALIDATED)
.fetch();
if (ObjectUtils.notEmpty(leaves)) {
for (LeaveRequest leave : leaves) {
LocalDate from = leave.getFromDateT().toLocalDate();
LocalDate to = leave.getToDateT().toLocalDate();
if ((from.isBefore(date) && to.isAfter(date)) || from.isEqual(date) || to.isEqual(date)) {
return leave;
}
}
}
return null;
}
}

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,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,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,144 @@
/*
* 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.db.Partner;
import com.axelor.apps.hr.db.DPAE;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
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.lang.invoke.MethodHandles;
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());
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);
}
}

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

View File

@@ -0,0 +1,293 @@
/*
* 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.extra.hours;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Wizard;
import com.axelor.apps.base.service.PeriodService;
import com.axelor.apps.base.service.message.MessageServiceBaseImpl;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.ExtraHours;
import com.axelor.apps.hr.db.repo.ExtraHoursRepository;
import com.axelor.apps.hr.service.HRMenuTagService;
import com.axelor.apps.hr.service.HRMenuValidateService;
import com.axelor.apps.hr.service.extra.hours.ExtraHoursService;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.db.repo.MessageRepository;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.db.Query;
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.util.List;
import java.util.Map;
@Singleton
public class ExtraHoursController {
public void editExtraHours(ActionRequest request, ActionResponse response) {
List<ExtraHours> extraHoursList =
Beans.get(ExtraHoursRepository.class)
.all()
.filter(
"self.user = ?1 AND self.company = ?2 AND self.statusSelect = 1",
AuthUtils.getUser(),
AuthUtils.getUser().getActiveCompany())
.fetch();
if (extraHoursList.isEmpty()) {
response.setView(
ActionView.define(I18n.get("Extra Hours"))
.model(ExtraHours.class.getName())
.add("form", "extra-hours-form")
.map());
} else if (extraHoursList.size() == 1) {
response.setView(
ActionView.define(I18n.get("ExtraHours"))
.model(ExtraHours.class.getName())
.add("form", "extra-hours-form")
.param("forceEdit", "true")
.context("_showRecord", String.valueOf(extraHoursList.get(0).getId()))
.map());
} else {
response.setView(
ActionView.define(I18n.get("ExtraHours"))
.model(Wizard.class.getName())
.add("form", "popup-extra-hours-form")
.param("forceEdit", "true")
.param("popup", "true")
.param("show-toolbar", "false")
.param("show-confirm", "false")
.param("forceEdit", "true")
.param("popup-save", "false")
.map());
}
}
public void validateExtraHours(ActionRequest request, ActionResponse response)
throws AxelorException {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Extra hours to Validate"))
.model(ExtraHours.class.getName())
.add("grid", "extra-hours-validate-grid")
.add("form", "extra-hours-form");
Beans.get(HRMenuValidateService.class).createValidateDomain(user, employee, actionView);
response.setView(actionView.map());
}
public void editExtraHoursSelected(ActionRequest request, ActionResponse response) {
Map extraHoursMap = (Map) request.getContext().get("extraHoursSelect");
ExtraHours extraHours =
Beans.get(ExtraHoursRepository.class).find(new Long((Integer) extraHoursMap.get("id")));
response.setView(
ActionView.define("Extra hours")
.model(ExtraHours.class.getName())
.add("form", "extra-hours-form")
.param("forceEdit", "true")
.domain("self.id = " + extraHoursMap.get("id"))
.context("_showRecord", String.valueOf(extraHours.getId()))
.map());
}
public void historicExtraHours(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Historic colleague extra hours"))
.model(ExtraHours.class.getName())
.add("grid", "extra-hours-grid")
.add("form", "extra-hours-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 showSubordinateExtraHours(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Company activeCompany = user.getActiveCompany();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Extra hours to be Validated by your subordinates"))
.model(ExtraHours.class.getName())
.add("grid", "extra-hours-grid")
.add("form", "extra-hours-form");
String domain =
"self.user.employee.managerUser.employee.managerUser = :_user AND self.company = :_activeCompany AND self.statusSelect = 2";
long nbExtraHours =
Query.of(ExtraHours.class)
.filter(domain)
.bind("_user", user)
.bind("_activeCompany", activeCompany)
.count();
if (nbExtraHours == 0) {
response.setNotify(I18n.get("No extra hours to be validated by your subordinates"));
} else {
response.setView(
actionView
.domain(domain)
.context("_user", user)
.context("_activeCompany", activeCompany)
.map());
}
}
/* Count Tags displayed on the menu items */
public String extraHoursValidateMenuTag() {
return Beans.get(HRMenuTagService.class)
.countRecordsTag(ExtraHours.class, ExtraHoursRepository.STATUS_CONFIRMED);
}
// confirming request and sending mail to manager
public void confirm(ActionRequest request, ActionResponse response) throws AxelorException {
try {
ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
extraHours = Beans.get(ExtraHoursRepository.class).find(extraHours.getId());
Beans.get(ExtraHoursService.class).confirm(extraHours);
Message message = Beans.get(ExtraHoursService.class).sendConfirmationEmail(extraHours);
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);
}
}
/**
* validating request and sending mail to applicant
*
* @param request
* @param response
* @throws AxelorException
*/
public void valid(ActionRequest request, ActionResponse response) throws AxelorException {
try {
ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
extraHours = Beans.get(ExtraHoursRepository.class).find(extraHours.getId());
Beans.get(ExtraHoursService.class).validate(extraHours);
Message message = Beans.get(ExtraHoursService.class).sendValidationEmail(extraHours);
if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
response.setFlash(
String.format(
I18n.get("Email sent to %s"),
Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
}
Beans.get(PeriodService.class)
.checkPeriod(extraHours.getCompany(), extraHours.getValidationDate());
} catch (Exception e) {
TraceBackService.trace(response, e);
} finally {
response.setReload(true);
}
}
// refusing request and sending mail to applicant
public void refuse(ActionRequest request, ActionResponse response) throws AxelorException {
try {
ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
extraHours = Beans.get(ExtraHoursRepository.class).find(extraHours.getId());
Beans.get(ExtraHoursService.class).refuse(extraHours);
Message message = Beans.get(ExtraHoursService.class).sendRefusalEmail(extraHours);
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);
}
}
// canceling request and sending mail to applicant
public void cancel(ActionRequest request, ActionResponse response) throws AxelorException {
try {
ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
extraHours = Beans.get(ExtraHoursRepository.class).find(extraHours.getId());
Beans.get(ExtraHoursService.class).cancel(extraHours);
Message message = Beans.get(ExtraHoursService.class).sendCancellationEmail(extraHours);
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);
}
}
// counting total hours while computing extra hours lines
public void compute(ActionRequest request, ActionResponse response) {
try {
ExtraHours extraHours = request.getContext().asType(ExtraHours.class);
Beans.get(ExtraHoursService.class).compute(extraHours);
response.setValue("totalQty", extraHours.getTotalQty());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@@ -0,0 +1,401 @@
/*
* 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.leave;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Wizard;
import com.axelor.apps.base.service.PeriodService;
import com.axelor.apps.base.service.message.MessageServiceBaseImpl;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.ExtraHours;
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.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.LeaveReasonRepository;
import com.axelor.apps.hr.db.repo.LeaveRequestRepository;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.service.HRMenuTagService;
import com.axelor.apps.hr.service.HRMenuValidateService;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.hr.service.leave.LeaveService;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.message.db.repo.MessageRepository;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.db.Query;
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 com.google.inject.persist.Transactional;
import java.util.List;
import java.util.Map;
@Singleton
public class LeaveController {
public void editLeave(ActionRequest request, ActionResponse response) {
try {
User user = AuthUtils.getUser();
List<LeaveRequest> leaveList =
Beans.get(LeaveRequestRepository.class)
.all()
.filter(
"self.user = ?1 AND self.company = ?2 AND self.statusSelect = 1",
user,
user.getActiveCompany())
.fetch();
if (leaveList.isEmpty()) {
response.setView(
ActionView.define(I18n.get("LeaveRequest"))
.model(LeaveRequest.class.getName())
.add("form", "leave-request-form")
.map());
} else if (leaveList.size() == 1) {
response.setView(
ActionView.define(I18n.get("LeaveRequest"))
.model(LeaveRequest.class.getName())
.add("form", "leave-request-form")
.param("forceEdit", "true")
.context("_showRecord", String.valueOf(leaveList.get(0).getId()))
.map());
} else {
response.setView(
ActionView.define(I18n.get("LeaveRequest"))
.model(Wizard.class.getName())
.add("form", "popup-leave-request-form")
.param("forceEdit", "true")
.param("popup", "true")
.param("show-toolbar", "false")
.param("show-confirm", "false")
.param("forceEdit", "true")
.param("popup-save", "false")
.map());
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
@SuppressWarnings("unchecked")
public void editLeaveSelected(ActionRequest request, ActionResponse response) {
try {
Map<String, Object> leaveMap = (Map<String, Object>) request.getContext().get("leaveSelect");
Long leaveId = Long.valueOf((long) leaveMap.get("id"));
response.setView(
ActionView.define(I18n.get("LeaveRequest"))
.model(LeaveRequest.class.getName())
.add("form", "leave-request-form")
.param("forceEdit", "true")
.domain("self.id = " + leaveId)
.context("_showRecord", leaveId)
.map());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void validateLeave(ActionRequest request, ActionResponse response) {
try {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Leave Requests to Validate"))
.model(LeaveRequest.class.getName())
.add("grid", "leave-request-validate-grid")
.add("form", "leave-request-form");
Beans.get(HRMenuValidateService.class).createValidateDomain(user, employee, actionView);
response.setView(actionView.map());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void historicLeave(ActionRequest request, ActionResponse response) {
try {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Colleague Leave Requests"))
.model(LeaveRequest.class.getName())
.add("grid", "leave-request-grid")
.add("form", "leave-request-form");
actionView.domain("(self.statusSelect = 3 OR self.statusSelect = 4)");
if (employee == null || !employee.getHrManager()) {
actionView
.domain(actionView.get().getDomain() + " AND self.user.employee.managerUser = :_user")
.context("_user", user);
}
response.setView(actionView.map());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void showSubordinateLeaves(ActionRequest request, ActionResponse response) {
try {
User user = AuthUtils.getUser();
String domain =
"self.user.employee.managerUser.employee.managerUser = :_user AND self.statusSelect = 2";
long nbLeaveRequests = Query.of(ExtraHours.class).filter(domain).bind("_user", user).count();
if (nbLeaveRequests == 0) {
response.setNotify(I18n.get("No Leave Request to be validated by your subordinates"));
} else {
ActionViewBuilder actionView =
ActionView.define(I18n.get("Leaves to be Validated by your subordinates"))
.model(LeaveRequest.class.getName())
.add("grid", "leave-request-grid")
.add("form", "leave-request-form");
response.setView(actionView.domain(domain).context("_user", user).map());
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void testDuration(ActionRequest request, ActionResponse response) {
try {
LeaveRequest leave = request.getContext().asType(LeaveRequest.class);
double duration = leave.getDuration().doubleValue();
if (duration % 0.5 != 0) {
response.setError(I18n.get("Invalid duration (must be a 0.5's multiple)"));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void computeDuration(ActionRequest request, ActionResponse response) {
try {
LeaveRequest leave = request.getContext().asType(LeaveRequest.class);
response.setValue("duration", Beans.get(LeaveService.class).computeDuration(leave));
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
// sending leave request and an email to the manager
public void send(ActionRequest request, ActionResponse response) {
try {
LeaveService leaveService = Beans.get(LeaveService.class);
LeaveRequest leaveRequest = request.getContext().asType(LeaveRequest.class);
leaveRequest = Beans.get(LeaveRequestRepository.class).find(leaveRequest.getId());
if (leaveRequest.getUser().getEmployee().getWeeklyPlanning() == null) {
response.setAlert(
String.format(
I18n.get(IExceptionMessage.EMPLOYEE_PLANNING),
leaveRequest.getUser().getEmployee().getName()));
return;
}
if (leaveRequest.getLeaveLine().getQuantity().subtract(leaveRequest.getDuration()).signum()
< 0) {
if (!leaveRequest.getLeaveLine().getLeaveReason().getAllowNegativeValue()
&& !leaveService.willHaveEnoughDays(leaveRequest)) {
String instruction = leaveRequest.getLeaveLine().getLeaveReason().getInstruction();
if (instruction == null) {
instruction = "";
}
response.setAlert(
String.format(
I18n.get(IExceptionMessage.LEAVE_ALLOW_NEGATIVE_VALUE_REASON),
leaveRequest.getLeaveLine().getLeaveReason().getLeaveReason())
+ " "
+ instruction);
return;
} else {
response.setNotify(
String.format(
I18n.get(IExceptionMessage.LEAVE_ALLOW_NEGATIVE_ALERT),
leaveRequest.getLeaveLine().getLeaveReason().getLeaveReason()));
}
}
leaveService.confirm(leaveRequest);
Message message = leaveService.sendConfirmationEmail(leaveRequest);
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);
}
}
/**
* Validates leave request and sends an email to the applicant.
*
* @param request
* @param response
* @throws AxelorException
*/
public void validate(ActionRequest request, ActionResponse response) {
try {
LeaveService leaveService = Beans.get(LeaveService.class);
LeaveRequest leaveRequest = request.getContext().asType(LeaveRequest.class);
leaveRequest = Beans.get(LeaveRequestRepository.class).find(leaveRequest.getId());
leaveService.validate(leaveRequest);
Message message = leaveService.sendValidationEmail(leaveRequest);
if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
response.setFlash(
String.format(
I18n.get("Email sent to %s"),
Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
}
Beans.get(PeriodService.class)
.checkPeriod(
leaveRequest.getCompany(),
leaveRequest.getToDateT().toLocalDate(),
leaveRequest.getFromDateT().toLocalDate());
} catch (Exception e) {
TraceBackService.trace(response, e);
} finally {
response.setReload(true);
}
}
/**
* Refuses leave request and sends an email to the applicant.
*
* @param request
* @param response
* @throws AxelorException
*/
public void refuse(ActionRequest request, ActionResponse response) throws AxelorException {
try {
LeaveService leaveService = Beans.get(LeaveService.class);
LeaveRequest leaveRequest = request.getContext().asType(LeaveRequest.class);
leaveRequest = Beans.get(LeaveRequestRepository.class).find(leaveRequest.getId());
leaveService.refuse(leaveRequest);
Message message = leaveService.sendRefusalEmail(leaveRequest);
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 cancel(ActionRequest request, ActionResponse response) {
try {
LeaveRequest leave = request.getContext().asType(LeaveRequest.class);
leave = Beans.get(LeaveRequestRepository.class).find(leave.getId());
LeaveService leaveService = Beans.get(LeaveService.class);
leaveService.cancel(leave);
Message message = leaveService.sendCancellationEmail(leave);
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);
}
}
/* Count Tags displayed on the menu items */
@Transactional
public void leaveReasonToJustify(ActionRequest request, ActionResponse response) {
try {
LeaveRequest leave = request.getContext().asType(LeaveRequest.class);
Boolean leaveToJustify = leave.getToJustifyLeaveReason();
LeaveLine leaveLine = null;
if (!leaveToJustify) {
return;
}
Company company = leave.getCompany();
if (leave.getUser() == null) {
return;
}
if (company == null) {
company = leave.getUser().getActiveCompany();
}
if (company == null) {
return;
}
Beans.get(HRConfigService.class).getLeaveReason(company.getHrConfig());
Employee employee = leave.getUser().getEmployee();
LeaveReason leaveReason =
Beans.get(LeaveReasonRepository.class)
.find(company.getHrConfig().getToJustifyLeaveReason().getId());
if (employee != null) {
employee = Beans.get(EmployeeRepository.class).find(leave.getUser().getEmployee().getId());
leaveLine = Beans.get(LeaveService.class).addLeaveReasonOrCreateIt(employee, leaveReason);
response.setValue("leaveLine", leaveLine);
}
} catch (AxelorException e) {
TraceBackService.trace(response, e);
}
}
public String leaveValidateMenuTag() {
return Beans.get(HRMenuTagService.class)
.countRecordsTag(LeaveRequest.class, LeaveRequestRepository.STATUS_AWAITING_VALIDATION);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.leave.management;
import com.axelor.apps.hr.db.LeaveLine;
import com.axelor.apps.hr.service.leave.management.LeaveManagementService;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
@Singleton
public class LeaveManagementController {
public void computeQuantityAvailable(ActionRequest request, ActionResponse response) {
LeaveLine leaveLine = request.getContext().asType(LeaveLine.class);
leaveLine = Beans.get(LeaveManagementService.class).computeQuantityAvailable(leaveLine);
response.setValue("quantity", leaveLine.getQuantity());
response.setValue("totalQuantity", leaveLine.getTotalQuantity());
response.setValue("leaveManagementList", leaveLine.getLeaveManagementList());
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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.lunch.voucher;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.hr.db.HRConfig;
import com.axelor.apps.hr.db.LunchVoucherAdvance;
import com.axelor.apps.hr.exception.IExceptionMessage;
import com.axelor.apps.hr.report.IReport;
import com.axelor.apps.hr.service.config.HRConfigService;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherAdvanceService;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherMgtService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.db.EntityHelper;
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.schema.actions.ActionView;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@Singleton
public class LunchVoucherAdvanceController {
public void checkOnNewAdvance(ActionRequest request, ActionResponse response)
throws AxelorException {
LunchVoucherAdvance lunchVoucherAdvance =
EntityHelper.getEntity(request.getContext().asType(LunchVoucherAdvance.class));
if (lunchVoucherAdvance.getEmployee().getMainEmploymentContract() == null) {
response.setError(
String.format(
I18n.get(IExceptionMessage.EMPLOYEE_CONTRACT_OF_EMPLOYMENT),
lunchVoucherAdvance.getEmployee().getName()));
return;
}
Company company = lunchVoucherAdvance.getEmployee().getMainEmploymentContract().getPayCompany();
HRConfig hrConfig = Beans.get(HRConfigService.class).getHRConfig(company);
int stock =
Beans.get(LunchVoucherMgtService.class)
.checkStock(company, lunchVoucherAdvance.getNbrLunchVouchers());
if (stock <= 0) {
response.setAlert(
String.format(
I18n.get(IExceptionMessage.LUNCH_VOUCHER_MIN_STOCK),
company.getName(),
hrConfig.getMinStockLunchVoucher(),
hrConfig.getAvailableStockLunchVoucher(),
TraceBackRepository.CATEGORY_INCONSISTENCY));
}
}
public void onNewAdvance(ActionRequest request, ActionResponse response) {
LunchVoucherAdvance lunchVoucherAdvance =
EntityHelper.getEntity(request.getContext().asType(LunchVoucherAdvance.class));
try {
Beans.get(LunchVoucherAdvanceService.class).onNewAdvance(lunchVoucherAdvance);
response.setCanClose(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void print(ActionRequest request, ActionResponse response) {
LunchVoucherAdvance lunchVoucherAdvance =
request.getContext().asType(LunchVoucherAdvance.class);
String name =
lunchVoucherAdvance.getEmployee().getName()
+ "-"
+ LocalDate.now().format(DateTimeFormatter.ISO_DATE);
try {
String fileLink =
ReportFactory.createReport(IReport.LUNCH_VOUCHER_ADVANCE, name)
.addParam("lunchVoucherAdvId", lunchVoucherAdvance.getId())
.addFormat(ReportSettings.FORMAT_PDF)
.generate()
.getFileLink();
response.setView(ActionView.define(name).add("html", fileLink).map());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@@ -0,0 +1,166 @@
/*
* 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.lunch.voucher;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.base.service.PeriodService;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.apps.hr.db.LunchVoucherMgt;
import com.axelor.apps.hr.db.LunchVoucherMgtLine;
import com.axelor.apps.hr.db.repo.LunchVoucherMgtLineRepository;
import com.axelor.apps.hr.db.repo.LunchVoucherMgtRepository;
import com.axelor.apps.hr.report.IReport;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherMgtService;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.exception.service.TraceBackService;
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;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Singleton
public class LunchVoucherMgtController {
public void calculate(ActionRequest request, ActionResponse response) {
try {
LunchVoucherMgt lunchVoucherMgt =
Beans.get(LunchVoucherMgtRepository.class)
.find(request.getContext().asType(LunchVoucherMgt.class).getId());
Beans.get(LunchVoucherMgtService.class).calculate(lunchVoucherMgt);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void validate(ActionRequest request, ActionResponse response) {
LunchVoucherMgt lunchVoucherMgt =
Beans.get(LunchVoucherMgtRepository.class)
.find(request.getContext().asType(LunchVoucherMgt.class).getId());
try {
Beans.get(LunchVoucherMgtService.class).validate(lunchVoucherMgt);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
return;
}
try {
Beans.get(PeriodService.class).checkPeriod(lunchVoucherMgt.getPayPeriod());
Beans.get(PeriodService.class).checkPeriod(lunchVoucherMgt.getLeavePeriod());
} catch (Exception e) {
response.setFlash(e.getMessage());
}
}
public void updateTotal(ActionRequest request, ActionResponse response) {
LunchVoucherMgt lunchVoucherMgt = request.getContext().asType(LunchVoucherMgt.class);
Beans.get(LunchVoucherMgtService.class).calculateTotal(lunchVoucherMgt);
response.setValue("totalLunchVouchers", lunchVoucherMgt.getTotalLunchVouchers());
response.setValue("requestedLunchVouchers", lunchVoucherMgt.getRequestedLunchVouchers());
response.setValue("givenLunchVouchers", lunchVoucherMgt.getGivenLunchVouchers());
}
public void export(ActionRequest request, ActionResponse response) throws IOException {
LunchVoucherMgt lunchVoucherMgt =
Beans.get(LunchVoucherMgtRepository.class)
.find(request.getContext().asType(LunchVoucherMgt.class).getId());
try {
Beans.get(LunchVoucherMgtService.class).export(lunchVoucherMgt);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void print(ActionRequest request, ActionResponse response) throws IOException {
LunchVoucherMgt lunchVoucherMgt = request.getContext().asType(LunchVoucherMgt.class);
String name =
lunchVoucherMgt.getCompany().getName()
+ " - "
+ LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
try {
String fileLink =
ReportFactory.createReport(IReport.LUNCH_VOUCHER_MGT_MONTHLY, name)
.addParam("lunchVoucherMgtId", lunchVoucherMgt.getId())
.addParam("Locale", Beans.get(UserService.class).getLanguage())
.addFormat(ReportSettings.FORMAT_PDF)
.generate()
.getFileLink();
response.setView(ActionView.define(name).add("html", fileLink).map());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void updateStock(ActionRequest request, ActionResponse response) {
try {
LunchVoucherMgt lunchVoucherMgt = request.getContext().asType(LunchVoucherMgt.class);
if (lunchVoucherMgt.getId() == null) {
return;
}
List<LunchVoucherMgtLine> oldLunchVoucherLines =
Beans.get(LunchVoucherMgtLineRepository.class)
.all()
.filter("self.lunchVoucherMgt.id = ?", lunchVoucherMgt.getId())
.fetch();
int stockQuantityStatus =
Beans.get(LunchVoucherMgtService.class)
.updateStock(
lunchVoucherMgt.getLunchVoucherMgtLineList(),
oldLunchVoucherLines,
lunchVoucherMgt.getCompany());
response.setValue("stockQuantityStatus", stockQuantityStatus);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void checkPayPeriod(ActionRequest request, ActionResponse response) {
LunchVoucherMgt lunchVoucherMgt = request.getContext().asType(LunchVoucherMgt.class);
try {
Beans.get(PeriodService.class).checkPeriod(lunchVoucherMgt.getPayPeriod());
} catch (Exception e) {
response.setFlash(e.getMessage());
}
}
public void checkLeavePeriod(ActionRequest request, ActionResponse response) {
LunchVoucherMgt lunchVoucherMgt = request.getContext().asType(LunchVoucherMgt.class);
try {
Beans.get(PeriodService.class).checkPeriod(lunchVoucherMgt.getLeavePeriod());
} catch (Exception e) {
response.setFlash(e.getMessage());
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.lunch.voucher;
import com.axelor.apps.hr.db.LunchVoucherMgtLine;
import com.axelor.apps.hr.service.lunch.voucher.LunchVoucherMgtLineService;
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;
@Singleton
public class LunchVoucherMgtLineController {
public void compute(ActionRequest request, ActionResponse response) {
try {
LunchVoucherMgtLine line = request.getContext().asType(LunchVoucherMgtLine.class);
Beans.get(LunchVoucherMgtLineService.class).compute(line);
response.setValue("lunchVoucherNumber", line.getLunchVoucherNumber());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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.project;
import com.axelor.apps.hr.service.project.ProjectPlanningTimeService;
import com.axelor.apps.project.db.ProjectPlanningTime;
import com.axelor.apps.project.db.repo.ProjectPlanningTimeRepository;
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.axelor.rpc.Context;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@Singleton
public class ProjectPlanningTimeController {
public void showPlanning(ActionRequest request, ActionResponse response) {
Context context = request.getContext();
Collection<Map<String, Object>> users =
(Collection<Map<String, Object>>) context.get("userSet");
String userIds = "";
if (users != null) {
for (Map<String, Object> user : users) {
if (userIds.isEmpty()) {
userIds = user.get("id").toString();
} else {
userIds += "," + user.get("id").toString();
}
}
}
ActionViewBuilder builder =
ActionView.define(I18n.get("Project Planning time"))
.model(ProjectPlanningTime.class.getName());
String url = "project/planning";
if (!userIds.isEmpty()) {
url += "?userIds=" + userIds;
}
builder.add("html", url);
response.setView(builder.map());
response.setCanClose(true);
}
public void addMultipleProjectPlanningTime(ActionRequest request, ActionResponse response)
throws AxelorException {
Context context = request.getContext();
Beans.get(ProjectPlanningTimeService.class).addMultipleProjectPlanningTime(context);
response.setCanClose(true);
}
/**
* Invert value of 'isIncludeInTuronverForecast' field and save the record.
*
* @param request
* @param response
* @throws AxelorException
*/
@Transactional
public void updateIsIncludeInTuronverForecast(ActionRequest request, ActionResponse response) {
try {
ProjectPlanningTime projectPlanningTime =
request.getContext().asType(ProjectPlanningTime.class);
projectPlanningTime =
Beans.get(ProjectPlanningTimeRepository.class).find(projectPlanningTime.getId());
projectPlanningTime.setIsIncludeInTurnoverForecast(
!projectPlanningTime.getIsIncludeInTurnoverForecast());
Beans.get(ProjectPlanningTimeRepository.class).save(projectPlanningTime);
response.setValue(
"isIncludeInTurnoverForecast", projectPlanningTime.getIsIncludeInTurnoverForecast());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void removeProjectPlanningTime(ActionRequest request, ActionResponse response) {
List<Map<String, Object>> projectPlanningTimeLines =
(List<Map<String, Object>>) request.getContext().get("projectPlanningTimeSet");
if (projectPlanningTimeLines != null) {
Beans.get(ProjectPlanningTimeService.class)
.removeProjectPlanningLines(projectPlanningTimeLines);
}
response.setReload(true);
}
}

View File

@@ -0,0 +1,609 @@
/*
* 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.timesheet;
import com.axelor.apps.ReportFactory;
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.db.repo.ProductRepository;
import com.axelor.apps.base.service.PeriodService;
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.ExtraHours;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
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.timesheet.TimesheetLineService;
import com.axelor.apps.hr.service.timesheet.TimesheetService;
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.project.db.Project;
import com.axelor.apps.project.db.repo.ProjectRepository;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.db.Query;
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.axelor.rpc.Context;
import com.google.inject.Singleton;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class TimesheetController {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public void prefillLines(ActionRequest request, ActionResponse response) {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
Beans.get(TimesheetService.class).prefillLines(timesheet);
response.setValues(timesheet);
} catch (AxelorException e) {
TraceBackService.trace(response, e);
}
}
@SuppressWarnings("unchecked")
public void generateLines(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
Context context = request.getContext();
LocalDate fromGenerationDate = null;
if (context.get("fromGenerationDate") != null)
fromGenerationDate =
LocalDate.parse(
context.get("fromGenerationDate").toString(), DateTimeFormatter.ISO_DATE);
LocalDate toGenerationDate = null;
if (context.get("toGenerationDate") != null)
toGenerationDate =
LocalDate.parse(context.get("toGenerationDate").toString(), DateTimeFormatter.ISO_DATE);
BigDecimal logTime = BigDecimal.ZERO;
if (context.get("logTime") != null)
logTime = new BigDecimal(context.get("logTime").toString());
Map<String, Object> projectContext = (Map<String, Object>) context.get("project");
Project project = null;
if (projectContext != null) {
project =
Beans.get(ProjectRepository.class)
.find(((Integer) projectContext.get("id")).longValue());
}
Map<String, Object> productContext = (Map<String, Object>) context.get("product");
Product product = null;
if (productContext != null) {
product =
Beans.get(ProductRepository.class)
.find(((Integer) productContext.get("id")).longValue());
}
if (context.get("showActivity") == null || !(Boolean) context.get("showActivity")) {
product = Beans.get(UserHrService.class).getTimesheetProduct(timesheet.getUser());
}
timesheet =
Beans.get(TimesheetService.class)
.generateLines(
timesheet, fromGenerationDate, toGenerationDate, logTime, project, product);
response.setValue("timesheetLineList", timesheet.getTimesheetLineList());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void editTimesheet(ActionRequest request, ActionResponse response) {
List<Timesheet> timesheetList =
Beans.get(TimesheetRepository.class)
.all()
.filter(
"self.user = ?1 AND self.company = ?2 AND self.statusSelect = 1",
AuthUtils.getUser(),
AuthUtils.getUser().getActiveCompany())
.fetch();
if (timesheetList.isEmpty()) {
response.setView(
ActionView.define(I18n.get("Timesheet"))
.model(Timesheet.class.getName())
.add("form", "timesheet-form")
.map());
} else if (timesheetList.size() == 1) {
response.setView(
ActionView.define(I18n.get("Timesheet"))
.model(Timesheet.class.getName())
.add("form", "timesheet-form")
.param("forceEdit", "true")
.context("_showRecord", String.valueOf(timesheetList.get(0).getId()))
.map());
} else {
response.setView(
ActionView.define(I18n.get("Timesheet"))
.model(Wizard.class.getName())
.add("form", "popup-timesheet-form")
.param("forceEdit", "true")
.param("popup", "true")
.param("show-toolbar", "false")
.param("show-confirm", "false")
.param("forceEdit", "true")
.param("popup-save", "false")
.map());
}
}
public void allTimesheet(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Timesheets"))
.model(Timesheet.class.getName())
.add("grid", "all-timesheet-grid")
.add("form", "timesheet-form");
if (employee == null || !employee.getHrManager()) {
if (employee == null || employee.getManagerUser() == null) {
actionView
.domain("self.user = :_user OR self.user.employee.managerUser = :_user")
.context("_user", user);
} else {
actionView.domain("self.user.employee.managerUser = :_user").context("_user", user);
}
}
response.setView(actionView.map());
}
public void allTimesheetLine(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("See timesheet lines"))
.model(TimesheetLine.class.getName())
.add("grid", "timesheet-line-grid")
.add("form", "timesheet-line-form");
Beans.get(TimesheetService.class).createDomainAllTimesheetLine(user, employee, actionView);
response.setView(actionView.map());
}
public void validateTimesheet(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Timesheets to Validate"))
.model(Timesheet.class.getName())
.add("grid", "timesheet-validate-grid")
.add("form", "timesheet-form")
.context("todayDate", Beans.get(AppBaseService.class).getTodayDate());
Beans.get(HRMenuValidateService.class).createValidateDomain(user, employee, actionView);
response.setView(actionView.map());
}
public void validateTimesheetLine(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("See timesheet lines"))
.model(TimesheetLine.class.getName())
.add("grid", "timesheet-line-grid")
.add("form", "timesheet-line-form")
.context("todayDate", Beans.get(AppBaseService.class).getTodayDate());
Beans.get(TimesheetService.class).createValidateDomainTimesheetLine(user, employee, actionView);
response.setView(actionView.map());
}
public void editTimesheetSelected(ActionRequest request, ActionResponse response) {
Map<?, ?> timesheetMap = (Map<?, ?>) request.getContext().get("timesheetSelect");
Timesheet timesheet =
Beans.get(TimesheetRepository.class).find(Long.valueOf((Integer) timesheetMap.get("id")));
response.setView(
ActionView.define("Timesheet")
.model(Timesheet.class.getName())
.add("form", "timesheet-form")
.param("forceEdit", "true")
.domain("self.id = " + timesheetMap.get("id"))
.context("_showRecord", String.valueOf(timesheet.getId()))
.map());
}
public void historicTimesheet(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Historic colleague Timesheets"))
.model(Timesheet.class.getName())
.add("grid", "timesheet-grid")
.add("form", "timesheet-form");
actionView.domain("(self.statusSelect = 3 OR self.statusSelect = 4)");
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 historicTimesheetLine(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Employee employee = user.getEmployee();
ActionViewBuilder actionView =
ActionView.define(I18n.get("See timesheet lines"))
.model(TimesheetLine.class.getName())
.add("grid", "timesheet-line-grid")
.add("form", "timesheet-line-form");
actionView
.domain(
"self.timesheet.company = :_activeCompany AND (self.timesheet.statusSelect = 3 OR self.timesheet.statusSelect = 4)")
.context("_activeCompany", user.getActiveCompany());
if (employee == null || !employee.getHrManager()) {
actionView
.domain(
actionView.get().getDomain()
+ " AND self.timesheet.user.employee.managerUser = :_user")
.context("_user", user);
}
response.setView(actionView.map());
}
public void showSubordinateTimesheets(ActionRequest request, ActionResponse response) {
User user = AuthUtils.getUser();
Company activeCompany = user.getActiveCompany();
ActionViewBuilder actionView =
ActionView.define(I18n.get("Timesheets to be Validated by your subordinates"))
.model(Timesheet.class.getName())
.add("grid", "timesheet-grid")
.add("form", "timesheet-form");
String domain =
"self.user.employee.managerUser.employee.managerUser = :_user AND self.company = :_activeCompany AND self.statusSelect = 2";
long nbTimesheets =
Query.of(ExtraHours.class)
.filter(domain)
.bind("_user", user)
.bind("_activeCompany", activeCompany)
.count();
if (nbTimesheets == 0) {
response.setNotify(I18n.get("No timesheet to be validated by your subordinates"));
} else {
response.setView(
actionView
.domain(domain)
.context("_user", user)
.context("_activeCompany", activeCompany)
.map());
}
}
public void cancel(ActionRequest request, ActionResponse response) {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
timesheet = Beans.get(TimesheetRepository.class).find(timesheet.getId());
Message message = Beans.get(TimesheetService.class).cancelAndSendCancellationEmail(timesheet);
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);
}
}
/**
* Called from timesheet form view, on clicking "return to draft" button. <br>
* Call {@link TimesheetService#draft(Timesheet)}
*
* @param request
* @param response
*/
public void draft(ActionRequest request, ActionResponse response) {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
timesheet = Beans.get(TimesheetRepository.class).find(timesheet.getId());
Beans.get(TimesheetService.class).draft(timesheet);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Action called when confirming a timesheet. Changing status + Sending mail to Manager
*
* @param request
* @param response
*/
public void confirm(ActionRequest request, ActionResponse response) {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
timesheet = Beans.get(TimesheetRepository.class).find(timesheet.getId());
Message message =
Beans.get(TimesheetService.class).confirmAndSendConfirmationEmail(timesheet);
if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
response.setFlash(
String.format(
I18n.get("Email sent to %s"),
Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
}
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(e);
response.setError(e.getMessage());
}
}
// Continue button
public void continueBtn(ActionRequest request, ActionResponse response) {
response.setView(
ActionView.define(I18n.get("Timesheet"))
.model(Timesheet.class.getName())
.add("form", "timesheet-form")
.add("grid", "timesheet-grid")
.domain("self.user = :_user")
.context("_user", AuthUtils.getUser())
.map());
}
// Confirm and continue button
public void confirmContinue(ActionRequest request, ActionResponse response) {
this.confirm(request, response);
this.continueBtn(request, response);
}
/**
* Action called when validating a timesheet. Changing status + Sending mail to Applicant
*
* @param request
* @param response
* @throws AxelorException
*/
public void valid(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
timesheet = Beans.get(TimesheetRepository.class).find(timesheet.getId());
TimesheetService timesheetService = Beans.get(TimesheetService.class);
timesheetService.checkEmptyPeriod(timesheet);
computeTimeSpent(request, response);
Message message = timesheetService.validateAndSendValidationEmail(timesheet);
if (message != null && message.getStatusSelect() == MessageRepository.STATUS_SENT) {
response.setFlash(
String.format(
I18n.get("Email sent to %s"),
Beans.get(MessageServiceBaseImpl.class).getToRecipients(message)));
}
Beans.get(PeriodService.class)
.checkPeriod(timesheet.getCompany(), timesheet.getToDate(), timesheet.getFromDate());
} catch (Exception e) {
TraceBackService.trace(response, e);
} finally {
response.setReload(true);
}
}
// action called when refusing a timesheet. Changing status + Sending mail to Applicant
public void refuse(ActionRequest request, ActionResponse response) throws AxelorException {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
timesheet = Beans.get(TimesheetRepository.class).find(timesheet.getId());
Message message = Beans.get(TimesheetService.class).refuseAndSendRefusalEmail(timesheet);
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 computeTimeSpent(ActionRequest request, ActionResponse response) {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
timesheet = Beans.get(TimesheetRepository.class).find(timesheet.getId());
if (timesheet.getTimesheetLineList() != null && !timesheet.getTimesheetLineList().isEmpty()) {
Beans.get(TimesheetService.class).computeTimeSpent(timesheet);
}
}
/* Count Tags displayed on the menu items */
public String timesheetValidateMenuTag() {
return Beans.get(HRMenuTagService.class)
.countRecordsTag(Timesheet.class, TimesheetRepository.STATUS_CONFIRMED);
}
public void printTimesheet(ActionRequest request, ActionResponse response)
throws AxelorException {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
String name = I18n.get("Timesheet") + " " + timesheet.getFullName().replace("/", "-");
String fileLink =
ReportFactory.createReport(IReport.TIMESHEET, name)
.addParam("TimesheetId", timesheet.getId())
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.toAttach(timesheet)
.generate()
.getFileLink();
logger.debug("Printing {}", name);
response.setView(ActionView.define(name).add("html", fileLink).map());
}
public void setShowActivity(ActionRequest request, ActionResponse response) {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
boolean showActivity = true;
User user = timesheet.getUser();
if (user != null) {
Company company = user.getActiveCompany();
if (company != null && company.getHrConfig() != null) {
showActivity = !company.getHrConfig().getUseUniqueProductForTimesheet();
}
}
response.setValue("$showActivity", showActivity);
}
public void openTimesheetEditor(ActionRequest request, ActionResponse response) {
Context context = request.getContext();
String url =
"hr/timesheet?timesheetId="
+ context.get("id")
+ "&showActivity="
+ context.get("showActivity");
response.setView(
ActionView.define(I18n.get("Timesheet lines"))
.add("html", url)
.param("popup", "reload")
.param("popup-save", "false")
.map());
}
public void timesheetPeriodTotalController(ActionRequest request, ActionResponse response) {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
TimesheetService timesheetService = Beans.get(TimesheetService.class);
BigDecimal periodTotal = timesheetService.computePeriodTotal(timesheet);
response.setAttr("periodTotal", "value", periodTotal);
response.setAttr("$periodTotalConvert", "hidden", false);
response.setAttr(
"$periodTotalConvert",
"value",
Beans.get(TimesheetLineService.class)
.computeHoursDuration(timesheet, periodTotal, false));
response.setAttr(
"$periodTotalConvert", "title", timesheetService.getPeriodTotalConvertTitle(timesheet));
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from timesheet form, on user change. Call {@link
* TimesheetService#updateTimeLoggingPreference(Timesheet)} to update the timesheet, and update
* the dummy field $periodTotalConvert
*
* @param request
* @param response
*/
public void updateTimeLoggingPreference(ActionRequest request, ActionResponse response) {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
Beans.get(TimesheetService.class).updateTimeLoggingPreference(timesheet);
response.setAttr("$periodTotalConvert", "hidden", false);
response.setAttr(
"$periodTotalConvert",
"value",
Beans.get(TimesheetLineService.class)
.computeHoursDuration(timesheet, timesheet.getPeriodTotal(), false));
response.setAttr(
"$periodTotalConvert",
"title",
Beans.get(TimesheetService.class).getPeriodTotalConvertTitle(timesheet));
response.setValue("timeLoggingPreferenceSelect", timesheet.getTimeLoggingPreferenceSelect());
response.setValue("timesheetLineList", timesheet.getTimesheetLineList());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void generateLinesFromExpectedPlanning(ActionRequest request, ActionResponse response) {
try {
Timesheet timesheet = request.getContext().asType(Timesheet.class);
timesheet = Beans.get(TimesheetRepository.class).find(timesheet.getId());
Beans.get(TimesheetService.class).generateLinesFromExpectedProjectPlanning(timesheet);
response.setReload(true);
} catch (AxelorException e) {
TraceBackService.trace(response, e);
}
}
}

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.web.timesheet;
import com.axelor.apps.hr.db.Timesheet;
import com.axelor.apps.hr.db.TimesheetLine;
import com.axelor.apps.hr.db.repo.TimesheetLineRepository;
import com.axelor.apps.hr.service.timesheet.TimesheetLineService;
import com.axelor.apps.hr.service.timesheet.TimesheetLineServiceImpl;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.axelor.rpc.Context;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
public class TimesheetLineController {
private static final String HOURS_DURATION_FIELD = "hoursDuration";
private static final String DURATION_FIELD = "duration";
/**
* Called from timesheet line editable grid or form. Get the timesheet corresponding to
* timesheetline and call {@link TimesheetLineService#computeHoursDuration(Timesheet, BigDecimal,
* boolean)}
*
* @param request
* @param response
*/
public void setStoredDuration(ActionRequest request, ActionResponse response) {
try {
TimesheetLine timesheetLine = request.getContext().asType(TimesheetLine.class);
Timesheet timesheet;
Context parent = request.getContext().getParent();
if (parent != null && parent.getContextClass().equals(Timesheet.class)) {
timesheet = parent.asType(Timesheet.class);
} else {
timesheet = timesheetLine.getTimesheet();
}
BigDecimal hoursDuration =
Beans.get(TimesheetLineService.class)
.computeHoursDuration(timesheet, timesheetLine.getDuration(), true);
response.setValue(HOURS_DURATION_FIELD, hoursDuration);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Called from timesheet editor Get the timesheet corresponding to timesheetline and call {@link
* TimesheetLineService#computeHoursDuration(Timesheet, BigDecimal, boolean)}
*
* @param request
* @param response
*/
public void setDuration(ActionRequest request, ActionResponse response) {
try {
TimesheetLine timesheetLine = request.getContext().asType(TimesheetLine.class);
Timesheet timesheet;
Context parent = request.getContext().getParent();
if (parent != null && parent.getContextClass().equals(Timesheet.class)) {
timesheet = parent.asType(Timesheet.class);
} else {
timesheet = timesheetLine.getTimesheet();
}
BigDecimal duration =
Beans.get(TimesheetLineService.class)
.computeHoursDuration(timesheet, timesheetLine.getHoursDuration(), false);
response.setValue(DURATION_FIELD, duration);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
/**
* Invert value of 'toInvoice' field and save the record
*
* @param request
* @param response
*/
@Transactional
public void updateToInvoice(ActionRequest request, ActionResponse response) {
try {
TimesheetLine timesheetLine = request.getContext().asType(TimesheetLine.class);
timesheetLine = Beans.get(TimesheetLineRepository.class).find(timesheetLine.getId());
timesheetLine.setToInvoice(!timesheetLine.getToInvoice());
Beans.get(TimesheetLineRepository.class).save(timesheetLine);
response.setValue("toInvoice", timesheetLine.getToInvoice());
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void setTimesheet(ActionRequest request, ActionResponse response) {
TimesheetLine timesheetLine = request.getContext().asType(TimesheetLine.class);
timesheetLine = Beans.get(TimesheetLineServiceImpl.class).setTimesheet(timesheetLine);
response.setValues(timesheetLine);
}
}

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.web.timesheet;
import com.axelor.apps.ReportFactory;
import com.axelor.apps.hr.db.TimesheetReport;
import com.axelor.apps.hr.report.IReport;
import com.axelor.apps.hr.service.timesheet.TimesheetReportService;
import com.axelor.apps.hr.translation.ITranslation;
import com.axelor.apps.message.db.Message;
import com.axelor.apps.report.engine.ReportSettings;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
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 java.lang.invoke.MethodHandles;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TimesheetReportController {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public void printEmployeeTimesheetReport(ActionRequest request, ActionResponse response)
throws AxelorException {
TimesheetReport timesheetReport = request.getContext().asType(TimesheetReport.class);
String name = I18n.get(ITranslation.TS_REPORT_TITLE);
String fileLink =
ReportFactory.createReport(IReport.EMPLOYEE_TIMESHEET, name)
.addParam("TimesheetReportId", timesheetReport.getId().toString())
.addParam("Locale", ReportSettings.getPrintingLocale(null))
.addParam(
"FromDate",
timesheetReport.getFromDate().format(DateTimeFormatter.ofPattern("dd-MM-yyy")))
.addParam(
"ToDate",
timesheetReport.getToDate().format(DateTimeFormatter.ofPattern("dd-MM-yyy")))
.toAttach(timesheetReport)
.generate()
.getFileLink();
logger.debug("Printing {}", name);
response.setView(ActionView.define(name).add("html", fileLink).map());
}
public void fillTimesheetReportReminderUsers(ActionRequest request, ActionResponse response) {
TimesheetReport timesheetReport = request.getContext().asType(TimesheetReport.class);
Set<User> reminderUserSet =
Beans.get(TimesheetReportService.class).getUserToBeReminded(timesheetReport);
if (!CollectionUtils.isEmpty(reminderUserSet)) {
response.setValue("reminderUserSet", reminderUserSet);
} else {
response.setValue("reminderUserSet", null);
response.setNotify(I18n.get(ITranslation.TS_REPORT_FILL_NO_USER));
}
}
public void sendTimesheetReminder(ActionRequest request, ActionResponse response)
throws AxelorException {
TimesheetReport timesheetReport = request.getContext().asType(TimesheetReport.class);
List<Message> messages = Beans.get(TimesheetReportService.class).sendReminders(timesheetReport);
List<Long> messageIds = new ArrayList<Long>();
messageIds.add(0L);
for (Message message : messages) {
messageIds.add(message.getId());
}
response.setView(
ActionView.define(I18n.get("Messages"))
.model(Message.class.getName())
.add("grid", "message-timesheet-reminder-grid")
.add("form", "message-form")
.domain("self.id in (:messageIds)")
.context("messageIds", messageIds)
.map());
}
}

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.web.timesheet.timer;
import com.axelor.apps.hr.db.TSTimer;
import com.axelor.apps.hr.db.repo.TSTimerRepository;
import com.axelor.apps.hr.service.timesheet.timer.TimesheetTimerService;
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;
@Singleton
public class TSTimerController {
public void editTimesheetTimer(ActionRequest request, ActionResponse response) {
TSTimer tsTimer = Beans.get(TimesheetTimerService.class).getCurrentTSTimer();
if (tsTimer == null) {
response.setView(
ActionView.define(I18n.get("TSTimer"))
.model(TSTimer.class.getName())
.add("form", "ts-timer-form")
.map());
} else {
response.setView(
ActionView.define(I18n.get("TSTimer"))
.model(TSTimer.class.getName())
.add("form", "ts-timer-form")
.param("forceEdit", "true")
.context("_showRecord", String.valueOf(tsTimer.getId()))
.map());
}
}
public void editTimesheetTimerFromTimesheet(ActionRequest request, ActionResponse response) {
ActionViewBuilder actionView =
ActionView.define(I18n.get("TSTimer"))
.model(TSTimer.class.getName())
.add("form", "ts-timer-form")
.param("popup", "reload")
.param("forceEdit", "true")
.param("width", "800")
.param("show-confirm", "true")
.param("show-toolbar", "false")
.param("popup-save", "true");
TSTimer tsTimer = Beans.get(TimesheetTimerService.class).getCurrentTSTimer();
if (tsTimer != null) {
actionView.context("_showRecord", String.valueOf(tsTimer.getId()));
}
response.setView(actionView.map());
}
public void pause(ActionRequest request, ActionResponse response) {
try {
TSTimer timerView = request.getContext().asType(TSTimer.class);
TSTimer timer = Beans.get(TSTimerRepository.class).find(timerView.getId());
Beans.get(TimesheetTimerService.class).pause(timer);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void stop(ActionRequest request, ActionResponse response) {
try {
TSTimer timerView = request.getContext().asType(TSTimer.class);
TSTimer timer = Beans.get(TSTimerRepository.class).find(timerView.getId());
Beans.get(TimesheetTimerService.class).stop(timer);
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@@ -0,0 +1,2 @@
"importId";"code";"name";"nextNum";"padding";"prefixe";"suffixe";"toBeAdded";"yearlyResetOk"
207;"expense";"Expense N°";1;4;"EXP%YY%FM";;1;1
1 importId code name nextNum padding prefixe suffixe toBeAdded yearlyResetOk
2 207 expense Expense N° 1 4 EXP%YY%FM 1 1

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
<input file="base_sequence.csv" separator=";" type="com.axelor.apps.base.db.Sequence" search="self.importId = :importId">
<bind to="yearlyResetOk" column="yearlyResetOk" eval="yearlyResetOk == '1' ? true : false"/>
<bind to="nextNum" column="nextNum" eval="nextNum?.empty ? '1' : nextNum"/>
<bind to="padding" column="padding" eval="padding?.empty ? '1' : padding"/>
<bind to="toBeAdded" column="toBeAdded" eval="toBeAdded?.empty ? '1' : toBeAdded"/>
<bind to="resetDate" eval="call:com.axelor.apps.base.service.app.AppBaseService:getTodayDate()" />
</input>
</csv-inputs>

View File

@@ -0,0 +1,2 @@
"importId";"code";"name";"nextNum";"padding";"prefixe";"suffixe";"toBeAdded";"yearlyResetOk"
207;"expense";"N° de note de frais";1;4;"NF%YY%FM";;1;1
1 importId code name nextNum padding prefixe suffixe toBeAdded yearlyResetOk
2 207 expense N° de note de frais 1 4 NF%YY%FM 1 1

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
<input file="auth_permission.csv" separator=";" type="com.axelor.auth.db.Permission" search="self.name = :name" call="com.axelor.csv.script.ImportPermission:importPermissionToRole">
<bind to="canRead" eval="can_read == 'x' ? 'true' : 'false'"/>
<bind to="canWrite" eval="can_write == 'x' ? 'true' : 'false'"/>
<bind to="canCreate" eval="can_create == 'x' ? 'true' : 'false'"/>
<bind to="canRemove" eval="can_remove == 'x' ? 'true' : 'false'"/>
<bind to="canExport" eval="can_export == 'x' ? 'true' : 'false'"/>
</input>
<input file="base_appEmployee.csv" separator=";" type="com.axelor.apps.base.db.AppEmployee" call="com.axelor.csv.script.ImportApp:importApp">
<bind column="dependsOn" to="dependsOnSet" search="self.code in :dependsOn" eval="dependsOn.split(',') as List"/>
</input>
<input file="base_appLeave.csv" separator=";" type="com.axelor.apps.base.db.AppLeave" call="com.axelor.csv.script.ImportApp:importApp">
<bind column="dependsOn" to="dependsOnSet" search="self.code in :dependsOn" eval="dependsOn.split(',') as List"/>
</input>
<input file="base_appExpense.csv" separator=";" type="com.axelor.apps.base.db.AppExpense" call="com.axelor.csv.script.ImportApp:importApp">
<bind column="dependsOn" to="dependsOnSet" search="self.code in :dependsOn" eval="dependsOn.split(',') as List"/>
</input>
<input file="base_appTimesheet.csv" separator=";" type="com.axelor.apps.base.db.AppTimesheet" call="com.axelor.csv.script.ImportApp:importApp">
<bind column="dependsOn" to="dependsOnSet" search="self.code in :dependsOn" eval="dependsOn.split(',') as List"/>
</input>
<input file="base_appExtraHours.csv" separator=";" type="com.axelor.apps.base.db.AppExtraHours" call="com.axelor.csv.script.ImportApp:importApp">
<bind column="dependsOn" to="dependsOnSet" search="self.code in :dependsOn" eval="dependsOn.split(',') as List"/>
</input>
<input file="hr_leaveReason.csv" type="com.axelor.apps.hr.db.LeaveReason" separator=";"/>
<input file="meta_helpEN.csv" separator=";" type="com.axelor.meta.db.MetaHelp">
<bind to="language" eval="'en'" />
<bind to="type" eval="'tooltip'" />
<bind to="model" eval="com.axelor.inject.Beans.get(com.axelor.meta.db.repo.MetaModelRepository.class).findByName(object)?.getFullName()" column="object" />
</input>
<input file="meta_helpFR.csv" separator=";" type="com.axelor.meta.db.MetaHelp">
<bind to="language" eval="'fr'" />
<bind to="type" eval="'tooltip'" />
<bind to="model" eval="com.axelor.inject.Beans.get(com.axelor.meta.db.repo.MetaModelRepository.class).findByName(object)?.getFullName()" column="object" />
</input>
<input file="meta_metaMenu.csv" separator=";" type="com.axelor.meta.db.MetaMenu" search="self.name = :name" update="true" />
</csv-inputs>

View File

@@ -0,0 +1,2 @@
"name";"object";"can_read";"can_write";"can_create";"can_remove";"can_export";"condition";"conditionParams";"roleName"
"perm.hr.all";"com.axelor.apps.hr.db.*";"x";"x";"x";"x";"x";;;"Admin"
1 name object can_read can_write can_create can_remove can_export condition conditionParams roleName
2 perm.hr.all com.axelor.apps.hr.db.* x x x x x Admin

View File

@@ -0,0 +1,2 @@
"name";"code";"installOrder";"description";"imagePath";"modules";"dependsOn";"sequence"
"Employees Managment";"employee";12;"Employees Management";"app-employee.png";"axelor-human-resource";"account";17
1 name code installOrder description imagePath modules dependsOn sequence
2 Employees Managment employee 12 Employees Management app-employee.png axelor-human-resource account 17

View File

@@ -0,0 +1,2 @@
"name";"code";"installOrder";"description";"imagePath";"modules";"dependsOn";"sequence"
"Expense Management";"expense";15;"Expense Management";"app-expense.png";"axelor-human-resource";"account,project,employee";19
1 name code installOrder description imagePath modules dependsOn sequence
2 Expense Management expense 15 Expense Management app-expense.png axelor-human-resource account,project,employee 19

View File

@@ -0,0 +1,2 @@
"name";"code";"installOrder";"description";"imagePath";"modules";"dependsOn";"sequence"
"Extra hours";"exthrs";16;"Extra hours";"app-extra-hours.png";"axelor-human-resource";"employee,project";21
1 name code installOrder description imagePath modules dependsOn sequence
2 Extra hours exthrs 16 Extra hours app-extra-hours.png axelor-human-resource employee,project 21

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