org.libreplan.business.workreports.entities.WorkReportLine.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.business.workreports.entities.WorkReportLine.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2009-2010 Fundacin para o Fomento da Calidade Industrial e
 *                         Desenvolvemento Tecnolxico de Galicia
 * Copyright (C) 2010-2011 Igalia, S.L.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 org.libreplan.business.workreports.entities;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull;
import javax.validation.Valid;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.joda.time.Seconds;
import org.libreplan.business.common.IntegrationEntity;
import org.libreplan.business.common.Registry;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.business.labels.entities.LabelType;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
import org.libreplan.business.workreports.valueobjects.DescriptionField;
import org.libreplan.business.workreports.valueobjects.DescriptionValue;

/**
 * Note: this class has a natural ordering that is inconsistent with equals.
 *
 * @author Diego Pino Garca <dpino@igalia.com>
 * @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
 */
public class WorkReportLine extends IntegrationEntity implements Comparable<WorkReportLine>, IWorkReportsElements {

    private EffortDuration effort;

    private Date date;

    private LocalTime clockStart;

    private LocalTime clockFinish;

    private Resource resource;

    private OrderElement orderElement;

    private Set<Label> labels = new HashSet<>();

    private Set<DescriptionValue> descriptionValues = new HashSet<>();

    private WorkReport workReport;

    private TypeOfWorkHours typeOfWorkHours;

    private Boolean finished = false;

    /**
     * Constructor for hibernate. Do not use!
     */
    public WorkReportLine() {

    }

    public WorkReportLine(WorkReport workReport) {
        this.setWorkReport(workReport);
    }

    public static WorkReportLine create(WorkReport workReport) {
        return create(new WorkReportLine(workReport));
    }

    @NotNull(message = "effort not specified")
    public EffortDuration getEffort() {
        return effort;
    }

    public void setEffort(EffortDuration effort) {
        this.effort = effort;

        if ((workReport != null) && (workReport.getWorkReportType() != null) && (workReport.getWorkReportType()
                .getHoursManagement().equals(HoursManagementEnum.HOURS_CALCULATED_BY_CLOCK))) {

            this.effort = getDiferenceBetweenTimeStartAndFinish();
        }
    }

    public LocalTime getClockFinish() {
        return clockFinish;
    }

    public void setClockFinish(Date clockFinish) {
        if (clockFinish != null) {
            setClockFinish(LocalTime.fromDateFields(clockFinish));
        }
    }

    public void setClockFinish(LocalTime clockFinish) {
        this.clockFinish = clockFinish;
        updateEffort();
    }

    public LocalTime getClockStart() {
        return clockStart;
    }

    public void setClockStart(Date clockStart) {
        if (clockStart != null) {
            setClockStart(LocalTime.fromDateFields(clockStart));
        }
    }

    public void setClockStart(LocalTime clockStart) {
        this.clockStart = clockStart;
        updateEffort();
    }

    @Override
    @NotNull(message = "date not specified")
    public Date getDate() {
        return date;
    }

    public LocalDate getLocalDate() {
        if (getDate() == null) {
            return null;
        }
        return LocalDate.fromDateFields(getDate());
    }

    @Override
    public void setDate(Date date) {
        this.date = date;

        if ((workReport != null) && (workReport.getWorkReportType() != null)
                && workReport.getWorkReportType().getDateIsSharedByLines()) {

            this.date = workReport.getDate();
        }
    }

    @Override
    @NotNull(message = "resource not specified")
    public Resource getResource() {
        return resource;
    }

    @Override
    public void setResource(Resource resource) {
        this.resource = resource;

        if ((workReport != null) && (workReport.getWorkReportType() != null)
                && workReport.getWorkReportType().getResourceIsSharedInLines()) {

            this.resource = workReport.getResource();
        }
    }

    @Override
    @NotNull(message = "task not specified")
    public OrderElement getOrderElement() {
        return orderElement;
    }

    @Override
    public void setOrderElement(OrderElement orderElement) {
        this.orderElement = orderElement;

        if ((workReport != null) && (workReport.getWorkReportType() != null)
                && workReport.getWorkReportType().getOrderElementIsSharedInLines()) {

            this.orderElement = workReport.getOrderElement();
        }

    }

    @Override
    public Set<Label> getLabels() {
        return labels;
    }

    @Override
    public void setLabels(Set<Label> labels) {
        this.labels = labels;
    }

    @NotNull(message = "timesheet not specified")
    public WorkReport getWorkReport() {
        return workReport;
    }

    private void setWorkReport(WorkReport workReport) {
        this.workReport = workReport;

        // Update and copy the fields and label for each line
        updateItsFieldsAndLabels();

        // Copy the required fields if these are shared by lines
        updatesAllSharedDataByLines();

        // Update calculated effort
        updateEffort();
    }

    @Override
    @Valid
    public Set<DescriptionValue> getDescriptionValues() {
        return descriptionValues;
    }

    @Override
    public void setDescriptionValues(Set<DescriptionValue> descriptionValues) {
        this.descriptionValues = descriptionValues;
    }

    @NotNull(message = "type of work hours not specified")
    public TypeOfWorkHours getTypeOfWorkHours() {
        return typeOfWorkHours;
    }

    public void setTypeOfWorkHours(TypeOfWorkHours typeOfWorkHours) {
        this.typeOfWorkHours = typeOfWorkHours;
    }

    @Override
    public int compareTo(WorkReportLine workReportLine) {
        if (date != null) {
            return date.compareTo(workReportLine.getDate());
        }
        return -1;
    }

    @AssertTrue(message = "closckStart:the clockStart must be not null if number of hours is calcultate by clock")
    public boolean isClockStartMustBeNotNullIfIsCalculatedByClockConstraint() {
        return !firstLevelValidationsPassed() || !workReport.getWorkReportType().getHoursManagement()
                .equals(HoursManagementEnum.HOURS_CALCULATED_BY_CLOCK) || (getClockStart() != null);

    }

    @AssertTrue(message = "clock finish cannot be empty if number of hours is calcultate by clock")
    public boolean isClockFinishMustBeNotNullIfIsCalculatedByClockConstraint() {
        return !firstLevelValidationsPassed() || !workReport.getWorkReportType().getHoursManagement()
                .equals(HoursManagementEnum.HOURS_CALCULATED_BY_CLOCK) || (getClockFinish() != null);

    }

    @AssertTrue(message = "Start hour cannot be greater than finish hour")
    public boolean isCannotBeHigherConstraint() {
        return !firstLevelValidationsPassed()
                || !workReport.getWorkReportType().getHoursManagement()
                        .equals(HoursManagementEnum.HOURS_CALCULATED_BY_CLOCK)
                || checkCannotBeHigher(this.clockStart, this.clockFinish);

    }

    public boolean checkCannotBeHigher(LocalTime starting, LocalTime ending) {
        return !((ending != null) && (starting != null) && (starting.compareTo(ending) > 0));
    }

    void updateItsFieldsAndLabels() {
        if (workReport != null) {
            assignItsLabels(workReport.getWorkReportType());
            assignItsDescriptionValues(workReport.getWorkReportType());
        }
    }

    private void assignItsLabels(WorkReportType workReportType) {
        Set<Label> updatedLabels = new HashSet<>();
        if (workReportType != null) {
            for (WorkReportLabelTypeAssignment labelTypeAssignment : workReportType.getLineLabels()) {
                Label label = getLabelBy(labelTypeAssignment);
                if (label != null) {
                    updatedLabels.add(label);
                } else {
                    updatedLabels.add(labelTypeAssignment.getDefaultLabel());
                }
            }
            this.labels = updatedLabels;
        }
    }

    private Label getLabelBy(WorkReportLabelTypeAssignment labelTypeAssignment) {
        LabelType type = labelTypeAssignment.getLabelType();
        for (Label label : labels) {
            if (label.getType().getId().equals(type.getId())) {
                return label;
            }
        }
        return null;
    }

    private void assignItsDescriptionValues(WorkReportType workReportType) {
        Set<DescriptionValue> updatedDescriptionValues = new HashSet<>();
        if (workReportType != null) {
            for (DescriptionField descriptionField : workReportType.getLineFields()) {
                DescriptionValue descriptionValue;
                try {
                    descriptionValue = this.getDescriptionValueByFieldName(descriptionField.getFieldName());
                } catch (InstanceNotFoundException e) {
                    descriptionValue = DescriptionValue.create(descriptionField.getFieldName(), null);
                }
                updatedDescriptionValues.add(descriptionValue);
            }
            this.descriptionValues = updatedDescriptionValues;
        }
    }

    void updatesAllSharedDataByLines() {
        // Copy the required fields if these are shared by lines
        updateSharedDateByLines();
        updateSharedResourceByLines();
        updateSharedOrderElementByLines();
    }

    void updateSharedDateByLines() {
        if ((workReport != null) && (workReport.getWorkReportType() != null)
                && (workReport.getWorkReportType().getDateIsSharedByLines())) {

            setDate(workReport.getDate());
        }
    }

    void updateSharedResourceByLines() {
        if ((workReport != null) && (workReport.getWorkReportType() != null)
                && (workReport.getWorkReportType().getResourceIsSharedInLines())) {

            setResource(workReport.getResource());
        }
    }

    void updateSharedOrderElementByLines() {
        if ((workReport != null) && (workReport.getWorkReportType() != null)
                && (workReport.getWorkReportType().getOrderElementIsSharedInLines())) {

            setOrderElement(workReport.getOrderElement());
        }
    }

    private void updateEffort() {
        if ((workReport != null) && (workReport.getWorkReportType() != null) && workReport.getWorkReportType()
                .getHoursManagement().equals(HoursManagementEnum.HOURS_CALCULATED_BY_CLOCK)) {

            setEffort(getDiferenceBetweenTimeStartAndFinish());
        }
    }

    private EffortDuration getDiferenceBetweenTimeStartAndFinish() {
        return (clockStart != null) && (clockFinish != null)
                ? EffortDuration.seconds(Seconds.secondsBetween(clockStart, clockFinish).getSeconds())
                : null;
    }

    @Override
    protected IWorkReportLineDAO getIntegrationEntityDAO() {
        return Registry.getWorkReportLineDAO();
    }

    @AssertTrue(message = "fields should match with timesheet data if are shared by lines")
    public boolean isFieldsMatchWithWorkReportIfAreSharedByLinesConstraint() {
        if (!firstLevelValidationsPassed()) {
            return true;
        }

        if (workReport.getWorkReportType().getDateIsSharedByLines()) {
            if (!workReport.getDate().equals(date)) {
                return false;
            }
        }
        if (workReport.getWorkReportType().getOrderElementIsSharedInLines()) {
            if (!workReport.getOrderElement().getId().equals(orderElement.getId())) {
                return false;
            }
        }
        if (workReport.getWorkReportType().getResourceIsSharedInLines()) {
            if (!workReport.getResource().getId().equals(resource.getId())) {
                return false;
            }
        }
        return true;
    }

    @AssertTrue(message = "Number of hours is not properly calculated according to start date and end date")
    public boolean isHoursCalculatedByClockConstraint() {
        if (!firstLevelValidationsPassed()) {
            return true;
        }

        if (workReport.getWorkReportType().getHoursManagement()
                .equals(HoursManagementEnum.HOURS_CALCULATED_BY_CLOCK)) {
            if (getDiferenceBetweenTimeStartAndFinish().compareTo(effort) != 0) {
                return false;
            }
        }
        return true;
    }

    private boolean firstLevelValidationsPassed() {
        return (workReport != null) && (typeOfWorkHours != null) && (effort != null) && (date != null)
                && (resource != null) && (orderElement != null);
    }

    @AssertTrue(message = "label type: the timesheet has not assigned this label type")
    public boolean isAssignedLabelTypesConstraint() {
        if (this.workReport == null || this.workReport.getWorkReportType() == null) {
            return true;
        }

        if (this.workReport.getWorkReportType().getLineLabels().size() != this.labels.size()) {
            return false;
        }

        for (WorkReportLabelTypeAssignment typeAssignment : this.workReport.getWorkReportType().getLineLabels()) {
            try {
                getLabelByType(typeAssignment.getLabelType());
            } catch (InstanceNotFoundException e) {
                return false;
            }
        }
        return true;
    }

    @AssertTrue(message = "description value: the timesheet has not assigned the description field")
    public boolean isAssignedDescriptionValuesConstraint() {
        if (this.workReport == null || this.workReport.getWorkReportType() == null) {
            return true;
        }

        if (this.workReport.getWorkReportType().getLineFields().size() > this.descriptionValues.size()) {
            return false;
        }

        for (DescriptionField field : this.workReport.getWorkReportType().getLineFields()) {
            try {
                getDescriptionValueByFieldName(field.getFieldName());
            } catch (InstanceNotFoundException e) {
                return false;
            }
        }
        return true;
    }

    @AssertTrue(message = "there are repeated description values in the timesheet lines")
    public boolean isAssignedRepeatedDescriptionValuesConstraint() {

        Set<String> textFields = new HashSet<>();

        for (DescriptionValue v : this.descriptionValues) {

            String name = v.getFieldName();

            if (!StringUtils.isBlank(name)) {
                if (textFields.contains(name.toLowerCase())) {
                    return false;
                } else {
                    textFields.add(name.toLowerCase());
                }
            }
        }
        return true;
    }

    public DescriptionValue getDescriptionValueByFieldName(String fieldName) throws InstanceNotFoundException {

        if (StringUtils.isBlank(fieldName)) {
            throw new InstanceNotFoundException(fieldName, DescriptionValue.class.getName());
        }

        for (DescriptionValue v : this.descriptionValues) {
            if (v.getFieldName().equalsIgnoreCase(StringUtils.trim(fieldName))) {
                return v;
            }
        }

        throw new InstanceNotFoundException(fieldName, DescriptionValue.class.getName());
    }

    public Label getLabelByType(LabelType type) throws InstanceNotFoundException {

        if (type == null) {
            throw new InstanceNotFoundException(type, LabelType.class.getName());
        }

        for (Label l : this.labels) {
            if (l.getType().getId().equals(type.getId())) {
                return l;
            }
        }

        throw new InstanceNotFoundException(type, LabelType.class.getName());
    }

    @Override
    public void setCodeAutogenerated(Boolean codeAutogenerated) {
        // Do nothing
    }

    @Override
    public Boolean isCodeAutogenerated() {
        return getWorkReport() != null ? getWorkReport().isCodeAutogenerated() : false;
    }

    @NotNull(message = "finished not specified")
    public Boolean isFinished() {
        return finished;
    }

    public void setFinished(Boolean finished) {
        this.finished = finished;
    }

    @AssertTrue(message = "there is a timesheet line in another work report marking as finished the same task")
    public boolean isOrderElementFinishedInAnotherWorkReportConstraint() {
        if (!finished) {
            return true;
        }

        List<WorkReportLine> lines = Registry.getWorkReportLineDAO()
                .findFinishedByOrderElementNotInWorkReportAnotherTransaction(orderElement, workReport);

        return lines.isEmpty();
    }

}