com.axelor.apps.hr.service.timesheet.TimesheetServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.axelor.apps.hr.service.timesheet.TimesheetServiceImpl.java

Source

/**
 * Axelor Business Solutions
 *
 * Copyright (C) 2016 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 java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.joda.time.Minutes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.InvoiceLine;
import com.axelor.apps.account.service.invoice.generator.InvoiceLineGenerator;
import com.axelor.apps.base.db.DayPlanning;
import com.axelor.apps.base.db.IPriceListLine;
import com.axelor.apps.base.db.PriceList;
import com.axelor.apps.base.db.PriceListLine;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.WeeklyPlanning;
import com.axelor.apps.base.db.repo.GeneralRepository;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.PriceListService;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.base.service.administration.GeneralService;
import com.axelor.apps.hr.db.Employee;
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.EmployeeRepository;
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.employee.EmployeeService;
import com.axelor.apps.hr.service.project.ProjectTaskService;
import com.axelor.apps.hr.db.repo.PublicHolidayDayRepository;
import com.axelor.apps.hr.db.PublicHolidayDay;
import com.axelor.apps.project.db.ProjectTask;
import com.axelor.apps.project.db.repo.ProjectTaskRepository;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.IException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;

public class TimesheetServiceImpl implements TimesheetService {

    @Inject
    protected EmployeeService employeeService;

    @Inject
    protected PriceListService priceListService;

    @Inject
    protected GeneralService generalService;

    @Inject
    protected ProjectTaskService projectTaskService;

    @Inject
    protected PublicHolidayDayRepository publicHolidayDayRepo;

    @Inject
    protected EmployeeRepository employeeRepo;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    @Transactional(rollbackOn = { Exception.class })
    public void getTimeFromTask(Timesheet timesheet) {

        List<TimesheetLine> timesheetLineList = TimesheetLineRepository.of(TimesheetLine.class).all()
                .filter("self.user = ?1 AND self.timesheet = null AND self.projectTask != null",
                        timesheet.getUser())
                .fetch();
        for (TimesheetLine timesheetLine : timesheetLineList) {
            timesheet.addTimesheetLineListItem(timesheetLine);
        }
        Beans.get(TimesheetRepository.class).save(timesheet);
    }

    @Override
    @Transactional(rollbackOn = { Exception.class })
    public void cancelTimesheet(Timesheet timesheet) {
        timesheet.setStatusSelect(TimesheetRepository.STATUS_CANCELED);
        Beans.get(TimesheetRepository.class).save(timesheet);
    }

    @Override
    public Timesheet generateLines(Timesheet timesheet, LocalDate fromGenerationDate, LocalDate toGenerationDate,
            BigDecimal logTime, ProjectTask projectTask, Product product) throws AxelorException {

        User user = timesheet.getUser();
        Employee employee = user.getEmployee();

        if (fromGenerationDate == null) {
            throw new AxelorException(String.format(I18n.get(IExceptionMessage.TIMESHEET_FROM_DATE)),
                    IException.MISSING_FIELD);
        }
        if (toGenerationDate == null) {
            throw new AxelorException(String.format(I18n.get(IExceptionMessage.TIMESHEET_TO_DATE)),
                    IException.MISSING_FIELD);
        }
        if (product == null) {
            throw new AxelorException(String.format(I18n.get(IExceptionMessage.TIMESHEET_PRODUCT)),
                    IException.MISSING_FIELD);
        }
        if (employee == null) {
            throw new AxelorException(
                    String.format(I18n.get(IExceptionMessage.LEAVE_USER_EMPLOYEE), user.getName()),
                    IException.CONFIGURATION_ERROR);
        }
        WeeklyPlanning planning = user.getEmployee().getPlanning();
        if (planning == null) {
            throw new AxelorException(
                    String.format(I18n.get(IExceptionMessage.TIMESHEET_EMPLOYEE_DAY_PLANNING), user.getName()),
                    IException.CONFIGURATION_ERROR);
        }
        List<DayPlanning> dayPlanningList = planning.getWeekDays();

        LocalDate fromDate = fromGenerationDate;
        LocalDate toDate = toGenerationDate;
        Map<Integer, String> correspMap = new HashMap<Integer, String>();
        correspMap.put(1, "monday");
        correspMap.put(2, "tuesday");
        correspMap.put(3, "wednesday");
        correspMap.put(4, "thursday");
        correspMap.put(5, "friday");
        correspMap.put(6, "saturday");
        correspMap.put(7, "sunday");

        //Leaving list
        List<LeaveRequest> leaveList = LeaveRequestRepository.of(LeaveRequest.class).all()
                .filter("self.user = ?1 AND (self.statusSelect = 2 OR self.statusSelect = 3)", user).fetch();

        //Public holidays list
        List<PublicHolidayDay> publicHolidayList = employee.getPublicHolidayPlanning().getPublicHolidayDayList();

        while (!fromDate.isAfter(toDate)) {
            DayPlanning dayPlanningCurr = new DayPlanning();
            for (DayPlanning dayPlanning : dayPlanningList) {
                if (dayPlanning.getName().equals(correspMap.get(fromDate.getDayOfWeek()))) {
                    dayPlanningCurr = dayPlanning;
                    break;
                }
            }
            if (dayPlanningCurr.getMorningFrom() != null || dayPlanningCurr.getMorningTo() != null
                    || dayPlanningCurr.getAfternoonFrom() != null || dayPlanningCurr.getAfternoonTo() != null) {
                /*Check if the day is not a leaving day */
                boolean noLeave = true;
                if (leaveList != null) {
                    for (LeaveRequest leave : leaveList) {
                        if ((leave.getFromDate().isBefore(fromDate) && leave.getToDate().isAfter(fromDate))
                                || leave.getFromDate().isEqual(fromDate) || leave.getToDate().isEqual(fromDate)) {
                            noLeave = false;
                            break;
                        }
                    }
                }

                /*Check if the day is not a public holiday */
                boolean noPublicHoliday = true;
                if (publicHolidayList != null) {
                    for (PublicHolidayDay publicHoliday : publicHolidayList) {
                        if (publicHoliday.getDate().isEqual(fromDate)) {
                            noPublicHoliday = false;
                            break;
                        }
                    }
                }

                if (noLeave && noPublicHoliday) {
                    TimesheetLine timesheetLine = createTimesheetLine(projectTask, product, user, fromDate,
                            timesheet, employeeService.getUserDuration(logTime, employee.getDailyWorkHours(), true),
                            "");
                    timesheetLine.setVisibleDuration(logTime);
                }

            }
            fromDate = fromDate.plusDays(1);
        }
        return timesheet;
    }

    @Override
    public LocalDate getFromPeriodDate() {
        Timesheet timesheet = Beans.get(TimesheetRepository.class).all()
                .filter("self.user = ?1 ORDER BY self.toDate DESC", AuthUtils.getUser()).fetchOne();
        if (timesheet != null) {
            return timesheet.getToDate();
        } else {
            return null;
        }
    }

    public Timesheet getCurrentTimesheet() {
        Timesheet timesheet = Beans.get(TimesheetRepository.class).all()
                .filter("self.statusSelect = ?1 AND self.user.id = ?2", TimesheetRepository.STATUS_DRAFT,
                        AuthUtils.getUser().getId())
                .order("-id").fetchOne();
        if (timesheet != null) {
            return timesheet;
        } else {
            return null;
        }
    }

    public Timesheet getCurrentOrCreateTimesheet() {
        Timesheet timesheet = getCurrentTimesheet();
        if (timesheet == null)
            timesheet = createTimesheet(AuthUtils.getUser(), generalService.getTodayDateTime().toLocalDate(), null);
        return timesheet;
    }

    public Timesheet createTimesheet(User user, LocalDate fromDate, LocalDate toDate) {
        Timesheet timesheet = new Timesheet();

        timesheet.setUser(user);
        timesheet.setCompany(user.getActiveCompany());
        timesheet.setFromDate(fromDate);
        timesheet.setStatusSelect(TimesheetRepository.STATUS_DRAFT);
        timesheet.setFullName(computeFullName(timesheet));

        return timesheet;
    }

    public TimesheetLine createTimesheetLine(ProjectTask 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.setProjectTask(project);
        timesheetLine.setUser(user);
        timesheetLine.setDurationStored(hours);
        timesheet.addTimesheetLineListItem(timesheetLine);

        return timesheetLine;
    }

    public List<InvoiceLine> createInvoiceLines(Invoice invoice, List<TimesheetLine> timesheetLineList,
            int priority) throws AxelorException {

        List<InvoiceLine> invoiceLineList = new ArrayList<InvoiceLine>();
        int count = 0;
        DateFormat ddmmFormat = new SimpleDateFormat("dd/MM");
        HashMap<String, Object[]> timeSheetInformationsMap = new HashMap<String, Object[]>();
        //Check if a consolidation by product and user must be done
        boolean consolidate = generalService.getGeneral().getConsolidateTSLine();

        for (TimesheetLine timesheetLine : timesheetLineList) {
            Object[] tabInformations = new Object[5];
            tabInformations[0] = timesheetLine.getProduct();
            tabInformations[1] = timesheetLine.getUser();
            //Start date
            tabInformations[2] = timesheetLine.getDate();
            //End date, useful only for consolidation
            tabInformations[3] = timesheetLine.getDate();
            tabInformations[4] = timesheetLine.getDurationStored();

            String key = null;
            if (consolidate) {
                key = timesheetLine.getProduct().getId() + "|" + timesheetLine.getUser().getId();
                if (timeSheetInformationsMap.containsKey(key)) {
                    tabInformations = timeSheetInformationsMap.get(key);
                    //Update date
                    if (timesheetLine.getDate().compareTo((LocalDate) tabInformations[2]) < 0) {
                        //If date is lower than start date then replace start date by this one
                        tabInformations[2] = timesheetLine.getDate();
                    } else if (timesheetLine.getDate().compareTo((LocalDate) tabInformations[3]) > 0) {
                        //If date is upper than end date then replace end date by this one
                        tabInformations[3] = timesheetLine.getDate();
                    }
                    tabInformations[4] = ((BigDecimal) tabInformations[4]).add(timesheetLine.getDurationStored());
                } else {
                    timeSheetInformationsMap.put(key, tabInformations);
                }
            } else {
                key = String.valueOf(timesheetLine.getId());
                timeSheetInformationsMap.put(key, tabInformations);
            }

            timesheetLine.setInvoiced(true);

        }

        for (Object[] timesheetInformations : timeSheetInformationsMap.values()) {

            String strDate = null;
            Product product = (Product) timesheetInformations[0];
            User user = (User) timesheetInformations[1];
            LocalDate startDate = (LocalDate) timesheetInformations[2];
            LocalDate endDate = (LocalDate) timesheetInformations[3];
            BigDecimal durationStored = (BigDecimal) timesheetInformations[4];

            if (consolidate) {
                strDate = ddmmFormat.format(startDate.toDate()) + " - " + ddmmFormat.format(endDate.toDate());
            } else {
                strDate = ddmmFormat.format(startDate.toDate());
            }

            invoiceLineList.addAll(this.createInvoiceLine(invoice, product, user, strDate, durationStored,
                    priority * 100 + count));
            count++;
        }

        return invoiceLineList;

    }

    public List<InvoiceLine> createInvoiceLine(Invoice invoice, Product product, User user, String date,
            BigDecimal durationStored, int priority) throws AxelorException {

        Employee employee = user.getEmployee();

        int discountTypeSelect = 1;
        if (product == null) {
            throw new AxelorException(String.format(I18n.get(IExceptionMessage.TIMESHEET_PRODUCT)),
                    IException.CONFIGURATION_ERROR);
        }
        BigDecimal price = product.getSalePrice();
        BigDecimal discountAmount = product.getCostPrice();

        BigDecimal qtyConverted = durationStored;
        qtyConverted = Beans.get(UnitConversionService.class).convert(generalService.getGeneral().getUnitHours(),
                product.getUnit(), durationStored);

        PriceList priceList = invoice.getPartner().getSalePriceList();
        if (priceList != null) {
            PriceListLine priceListLine = priceListService.getPriceListLine(product, qtyConverted, priceList);
            if (priceListLine != null) {
                discountTypeSelect = priceListLine.getTypeSelect();
            }
            if ((generalService.getGeneral()
                    .getComputeMethodDiscountSelect() == GeneralRepository.INCLUDE_DISCOUNT_REPLACE_ONLY
                    && discountTypeSelect == IPriceListLine.TYPE_REPLACE)
                    || generalService.getGeneral()
                            .getComputeMethodDiscountSelect() == GeneralRepository.INCLUDE_DISCOUNT) {
                Map<String, Object> discounts = priceListService.getDiscounts(priceList, priceListLine, price);
                if (discounts != null) {
                    discountAmount = (BigDecimal) discounts.get("discountAmount");
                    price = priceListService.computeDiscount(price, (int) discounts.get("discountTypeSelect"),
                            discountAmount);
                }
            } else {
                Map<String, Object> discounts = priceListService.getDiscounts(priceList, priceListLine, price);
                if (discounts != null) {
                    discountAmount = (BigDecimal) discounts.get("discountAmount");
                    if (discounts.get("price") != null) {
                        price = (BigDecimal) discounts.get("price");
                    }
                }
            }

        }

        String description = user.getFullName(), productName = product.getName() + " " + "(" + date + ")";

        InvoiceLineGenerator invoiceLineGenerator = new InvoiceLineGenerator(invoice, product, productName, price,
                price, description, qtyConverted, product.getUnit(), null, priority, discountAmount,
                discountTypeSelect, price.multiply(qtyConverted), null, false) {

            @Override
            public List<InvoiceLine> creates() throws AxelorException {

                InvoiceLine invoiceLine = this.createInvoiceLine();

                List<InvoiceLine> invoiceLines = new ArrayList<InvoiceLine>();
                invoiceLines.add(invoiceLine);

                return invoiceLines;
            }
        };

        return invoiceLineGenerator.creates();
    }

    @Override
    @Transactional
    public void computeTimeSpent(Timesheet timesheet) {
        List<TimesheetLine> timesheetLineList = timesheet.getTimesheetLineList();
        for (TimesheetLine timesheetLine : timesheetLineList) {
            ProjectTask projectTask = timesheetLine.getProjectTask();
            if (projectTask != null) {
                projectTask
                        .setTimeSpent(timesheetLine.getDurationStored().add(this.computeSubTimeSpent(projectTask)));
                this.computeParentTimeSpent(projectTask);
                Beans.get(ProjectTaskRepository.class).save(projectTask);
            }
        }
    }

    public BigDecimal computeSubTimeSpent(ProjectTask projectTask) {
        BigDecimal sum = BigDecimal.ZERO;
        List<ProjectTask> subProjectTaskList = Beans.get(ProjectTaskRepository.class).all()
                .filter("self.project = ?1", projectTask).fetch();
        if (subProjectTaskList == null || subProjectTaskList.isEmpty()) {
            return projectTask.getTimeSpent();
        }
        for (ProjectTask projectTaskIt : subProjectTaskList) {
            sum = sum.add(this.computeSubTimeSpent(projectTaskIt));
        }
        return sum;
    }

    public void computeParentTimeSpent(ProjectTask projectTask) {
        ProjectTask parentProject = projectTask.getProject();
        if (parentProject == null) {
            return;
        }
        parentProject.setTimeSpent(projectTask.getTimeSpent().add(this.computeTimeSpent(parentProject)));
        this.computeParentTimeSpent(parentProject);
    }

    public BigDecimal computeTimeSpent(ProjectTask projectTask) {
        BigDecimal sum = BigDecimal.ZERO;
        List<TimesheetLine> timesheetLineList = Beans.get(TimesheetLineRepository.class).all()
                .filter("self.projectTask = ?1 AND self.timesheet.statusSelect = ?2", projectTask,
                        TimesheetRepository.STATUS_VALIDATED)
                .fetch();
        for (TimesheetLine timesheetLine : timesheetLineList) {
            sum = sum.add(timesheetLine.getDurationStored());
        }
        return sum;
    }

    public void getActivities(ActionRequest request, ActionResponse response) {
        List<Map<String, String>> dataList = new ArrayList<Map<String, String>>();
        try {
            List<Product> productList = Beans.get(ProductRepository.class).all().filter("self.isActivity = true")
                    .fetch();
            for (Product product : productList) {
                Map<String, String> map = new HashMap<String, String>();
                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());
        }
    }

    @Transactional
    public void insertTSLine(ActionRequest request, ActionResponse response) {

        User user = AuthUtils.getUser();
        ProjectTask projectTask = Beans.get(ProjectTaskRepository.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 = new LocalDate(request.getData().get("date").toString());
        if (user != null) {
            Timesheet timesheet = Beans.get(TimesheetRepository.class).all()
                    .filter("self.statusSelect = 1 AND self.user.id = ?1", user.getId()).order("-id").fetchOne();
            if (timesheet == null) {
                timesheet = createTimesheet(user, date, date);
            }
            BigDecimal minutes = new BigDecimal(Minutes.minutesBetween(new LocalTime(0, 0),
                    new LocalTime(request.getData().get("duration").toString())).getMinutes());
            createTimesheetLine(projectTask, product, user, date, timesheet, minutes,
                    request.getData().get("comments").toString());

            Beans.get(TimesheetRepository.class).save(timesheet);
        }
    }

    public String computeFullName(Timesheet timesheet) {

        User timesheetUser = timesheet.getUser();
        LocalDateTime createdOn = timesheet.getCreatedOn();

        if (timesheetUser != null && createdOn != null) {
            return timesheetUser.getFullName() + " " + createdOn.getDayOfMonth() + "/" + createdOn.getMonthOfYear()
                    + "/" + timesheet.getCreatedOn().getYear() + " " + createdOn.getHourOfDay() + ":"
                    + createdOn.getMinuteOfHour();
        } else if (timesheetUser != null) {
            return timesheetUser.getFullName() + " N" + timesheet.getId();
        } else {
            return "N" + timesheet.getId();
        }
    }

    public List<TimesheetLine> computeVisibleDuration(Timesheet timesheet) {
        List<TimesheetLine> timesheetLineList = timesheet.getTimesheetLineList();

        Employee timesheetEmployee = timesheet.getUser().getEmployee();
        BigDecimal employeeDailyWorkHours;

        if (timesheetEmployee == null || timesheetEmployee.getDailyWorkHours() == null) {
            employeeDailyWorkHours = generalService.getGeneral().getDailyWorkHours();
        } else {
            employeeDailyWorkHours = timesheetEmployee.getDailyWorkHours();
        }

        for (TimesheetLine timesheetLine : timesheetLineList) {
            timesheetLine.setVisibleDuration(employeeService.getUserDuration(timesheetLine.getDurationStored(),
                    employeeDailyWorkHours, false));
        }

        timesheetLineList = projectTaskService._sortTimesheetLineByDate(timesheetLineList);

        return timesheetLineList;
    }
}