/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico 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.orders.entities;
import static org.libreplan.business.i18n.I18nHelper._;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hibernate.validator.constraints.NotEmpty;
import org.joda.time.LocalDate;
import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes;
import org.libreplan.business.advance.entities.AdvanceAssignment;
import org.libreplan.business.advance.entities.AdvanceMeasurement;
import org.libreplan.business.advance.entities.AdvanceType;
import org.libreplan.business.advance.entities.DirectAdvanceAssignment;
import org.libreplan.business.advance.entities.IndirectAdvanceAssignment;
import org.libreplan.business.advance.exceptions.DuplicateAdvanceAssignmentForOrderElementException;
import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException;
import org.libreplan.business.common.IntegrationEntity;
import org.libreplan.business.common.Registry;
import org.libreplan.business.common.daos.IIntegrationEntityDAO;
import org.libreplan.business.common.entities.Configuration;
import org.libreplan.business.common.entities.PredefinedConnectorProperties;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.costcategories.daos.IHourCostDAO;
import org.libreplan.business.costcategories.entities.CostCategory;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.business.materials.entities.MaterialAssignment;
import org.libreplan.business.orders.entities.SchedulingState.Type;
import org.libreplan.business.orders.entities.TaskSource.TaskSourceSynchronization;
import org.libreplan.business.planner.entities.Task;
import org.libreplan.business.planner.entities.TaskElement;
import org.libreplan.business.planner.entities.TaskPositionConstraint;
import org.libreplan.business.qualityforms.entities.QualityForm;
import org.libreplan.business.qualityforms.entities.TaskQualityForm;
import org.libreplan.business.requirements.entities.CriterionRequirement;
import org.libreplan.business.requirements.entities.DirectCriterionRequirement;
import org.libreplan.business.requirements.entities.IndirectCriterionRequirement;
import org.libreplan.business.resources.entities.Criterion;
import org.libreplan.business.scenarios.entities.OrderVersion;
import org.libreplan.business.scenarios.entities.Scenario;
import org.libreplan.business.templates.entities.OrderElementTemplate;
import org.libreplan.business.trees.ITreeNode;
import org.libreplan.business.util.deepcopy.DeepCopy;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workingday.IntraDayDate;
import org.libreplan.business.workreports.entities.WorkReportLine;
public abstract class OrderElement extends IntegrationEntity implements ICriterionRequirable, ITreeNode<OrderElement> {
protected InfoComponentWithCode infoComponent = new InfoComponentWithCode();
private Date initDate;
private Date deadline;
@Valid
protected Set<DirectAdvanceAssignment> directAdvanceAssignments = new HashSet<>();
@Valid
protected Set<MaterialAssignment> materialAssignments = new HashSet<>();
@Valid
protected Set<Label> labels = new HashSet<>();
protected Set<TaskQualityForm> taskQualityForms = new HashSet<>();
protected Set<CriterionRequirement> criterionRequirements = new HashSet<>();
protected OrderLineGroup parent;
protected CriterionRequirementOrderElementHandler criterionRequirementHandler =
CriterionRequirementOrderElementHandler.getInstance();
/**
* This field is transient.
*/
private SchedulingState schedulingState = null;
private OrderElementTemplate template;
private BigDecimal lastAdvanceMeasurementForSpreading = BigDecimal.ZERO;
private Boolean dirtyLastAdvanceMeasurementForSpreading = true;
private SumChargedEffort sumChargedEffort;
private SumExpenses sumExpenses;
private String externalCode;
private Map<OrderVersion, SchedulingDataForVersion> schedulingDataForVersion = new HashMap<>();
private SchedulingDataForVersion.Data current = null;
public OrderElementTemplate getTemplate() {
return template;
}
protected void removeVersion(OrderVersion orderVersion) {
schedulingDataForVersion.remove(orderVersion);
for (OrderElement each : getChildren()) {
each.removeVersion(orderVersion);
}
}
public SchedulingDataForVersion.Data getCurrentSchedulingData() {
if ( current == null ) {
throw new IllegalStateException(
"in order to use scheduling state related data " +
"useSchedulingDataFor(OrderVersion orderVersion) must be called first");
}
return current;
}
private void schedulingDataNowPointsTo(DeepCopy deepCopy, OrderVersion version) {
current = getCurrentSchedulingData().pointsTo(deepCopy, version, schedulingVersionFor(version));
for (OrderElement each : getChildren()) {
each.schedulingDataNowPointsTo(deepCopy, version);
}
}
protected void addNeededReplaces(DeepCopy deepCopy, OrderVersion newOrderVersion) {
SchedulingDataForVersion currentVersion = getCurrentSchedulingData().getVersion();
SchedulingDataForVersion newSchedulingVersion = schedulingVersionFor(newOrderVersion);
deepCopy.replace(currentVersion, newSchedulingVersion);
for (OrderElement each : getChildren()) {
each.addNeededReplaces(deepCopy, newOrderVersion);
}
}
public SchedulingState getSchedulingState() {
if ( schedulingState == null ) {
ensureSchedulingStateInitializedFromTop();
initializeSchedulingState(); // Maybe this order element was added later
}
return schedulingState;
}
private void ensureSchedulingStateInitializedFromTop() {
OrderElement current = this;
while (current.getParent() != null) {
current = current.getParent();
}
current.initializeSchedulingState();
}
private SchedulingState initializeSchedulingState() {
if ( schedulingState != null ) {
return schedulingState;
}
schedulingState = SchedulingState.createSchedulingState(
getSchedulingStateType(), getChildrenStates(), getCurrentSchedulingData().onTypeChangeListener());
return schedulingState;
}
private List<SchedulingState> getChildrenStates() {
List<SchedulingState> result = new ArrayList<>();
for (OrderElement each : getChildren()) {
result.add(each.initializeSchedulingState());
}
return result;
}
public boolean hasSchedulingDataBeingModified() {
return getCurrentSchedulingData().hasPendingChanges() || someSchedulingDataModified();
}
private boolean someSchedulingDataModified() {
for (OrderElement each : getChildren()) {
if (each.hasSchedulingDataBeingModified()) {
return true;
}
}
return false;
}
protected boolean isSchedulingDataInitialized() {
return current != null;
}
public void useSchedulingDataFor(OrderVersion orderVersion) {
useSchedulingDataFor(orderVersion, true);
}
public void useSchedulingDataFor(OrderVersion orderVersion, boolean recursive) {
Validate.notNull(orderVersion);
SchedulingDataForVersion schedulingVersion = schedulingVersionFor(orderVersion);
if ( recursive ) {
for (OrderElement each : getChildren()) {
each.useSchedulingDataFor(orderVersion);
}
}
current = schedulingVersion.makeAvailableFor(orderVersion);
}
private SchedulingDataForVersion schedulingVersionFor(OrderVersion orderVersion) {
SchedulingDataForVersion currentSchedulingData = schedulingDataForVersion.get(orderVersion);
if (currentSchedulingData == null) {
currentSchedulingData = SchedulingDataForVersion.createInitialFor(this);
schedulingDataForVersion.put(orderVersion, currentSchedulingData);
}
return currentSchedulingData;
}
public SchedulingDataForVersion getCurrentSchedulingDataForVersion() {
return getCurrentSchedulingData().getVersion();
}
protected void writeSchedulingDataChanges() {
getCurrentSchedulingData().writeSchedulingDataChanges();
for (OrderElement each : getChildren()) {
each.writeSchedulingDataChanges();
}
}
protected void writeSchedulingDataChangesTo(DeepCopy deepCopy, OrderVersion newOrderVersion) {
schedulingDataNowPointsTo(deepCopy, newOrderVersion);
writeSchedulingDataChanges();
}
protected void removeSpuriousDayAssignments(Scenario scenario) {
removeAtNotCurrent(scenario);
removeAtCurrent(scenario);
for (OrderElement each : getChildren()) {
each.removeSpuriousDayAssignments(scenario);
}
}
private void removeAtNotCurrent(Scenario scenario) {
SchedulingDataForVersion currentDataForVersion = getCurrentSchedulingDataForVersion();
for (Entry<OrderVersion, SchedulingDataForVersion> each : schedulingDataForVersion.entrySet()) {
SchedulingDataForVersion dataForVersion = each.getValue();
if (!currentDataForVersion.equals(dataForVersion)) {
dataForVersion.removeSpuriousDayAssignments(scenario);
}
}
}
private void removeAtCurrent(Scenario scenario) {
TaskElement associatedTaskElement = getAssociatedTaskElement();
if (associatedTaskElement != null) {
associatedTaskElement.removePredecessorsDayAssignmentsFor(scenario);
}
}
public List<TaskSourceSynchronization> calculateSynchronizationsNeeded() {
return calculateSynchronizationsNeeded(getCurrentSchedulingData().getVersion());
}
private List<TaskSourceSynchronization> calculateSynchronizationsNeeded(
SchedulingDataForVersion schedulingDataForVersion) {
List<TaskSourceSynchronization> result = new ArrayList<>();
if (isSchedulingPoint()) {
if (!wasASchedulingPoint()) {
// This element was a container but now it's a scheduling point
// we have to remove the TaskSource which contains a TaskGroup instead of TaskElement
removeTaskSource(result);
} else {
if (hadATaskSource() && currentTaskSourceIsNotTheSame()) {
// This element was unscheduled and then scheduled again.
// Its TaskSource has been recreated but we have to remove the old one.
if (!getParent().currentTaskSourceIsNotTheSame()) {
// We only remove the TaskSource if the parent is not in the same situation.
// In case the parent is in the same situation, it will remove the related
// TaskSources in children tasks.
removeTaskSource(result);
}
}
}
result.addAll(synchronizationForSchedulingPoint(schedulingDataForVersion));
} else if (isSuperElementPartialOrCompletelyScheduled()) {
removeUnscheduled(result);
if (wasASchedulingPoint()) {
removeTaskSource(result);
} else {
if (hadATaskSource() && currentTaskSourceIsNotTheSame()) {
// All the children of this element were unscheduled and then scheduled again,
// its TaskSource has been recreated but we have to remove the old one.
if (getParent() == null || !getParent().currentTaskSourceIsNotTheSame()) {
// If it's a container node inside another container we could have the
// same problem than in the case of leaf tasks.
result.add(taskSourceRemoval());
}
}
}
result.add(synchronizationForSuperelement(schedulingDataForVersion));
} else if (schedulingState.isNoScheduled()) {
removeTaskSource(result);
}
return result;
}
private TaskSourceSynchronization synchronizationForSuperelement(SchedulingDataForVersion schedulingState) {
List<TaskSourceSynchronization> childrenSynchronizations = childrenSynchronizations();
if (thereIsNoTaskSource()) {
getCurrentSchedulingData().requestedCreationOf(TaskSource.createForGroup(schedulingState));
return TaskSource.mustAddGroup(getTaskSource(), childrenSynchronizations);
} else {
return getTaskSource().modifyGroup(childrenSynchronizations);
}
}
private boolean wasASchedulingPoint() {
TaskSource currentTaskSource = getTaskSource();
// Check if the existing TaskSource is inconsistent with the current scheduling state
if (currentTaskSource != null && currentTaskSource.getTask() != null &&
currentTaskSource.getTask().isLeaf() &&
getSchedulingStateType() != Type.SCHEDULING_POINT) {
return true;
}
// Check if the scheduling state has changed WRT the DB
return SchedulingState.Type.SCHEDULING_POINT == getCurrentVersionOnDB().getSchedulingStateType();
}
protected boolean currentTaskSourceIsNotTheSame() {
return getOnDBTaskSource() != getTaskSource();
}
protected boolean hadATaskSource() {
return getOnDBTaskSource() != null;
}
private List<TaskSourceSynchronization> childrenSynchronizations() {
List<TaskSourceSynchronization> childrenOfGroup = new ArrayList<>();
for (OrderElement orderElement : getSomewhatScheduledOrderElements()) {
childrenOfGroup.addAll(orderElement.calculateSynchronizationsNeeded());
}
return childrenOfGroup;
}
private void removeUnscheduled(List<TaskSourceSynchronization> result) {
for (OrderElement orderElement : getNoScheduledOrderElements()) {
orderElement.removeTaskSource(result);
}
}
private List<TaskSourceSynchronization> synchronizationForSchedulingPoint(
SchedulingDataForVersion schedulingState) {
if (thereIsNoTaskSource()) {
getCurrentSchedulingData().requestedCreationOf(TaskSource.create(schedulingState, getHoursGroups()));
return Collections.singletonList(TaskSource.mustAdd(getTaskSource()));
} else if (getTaskSource().getTask().isLeaf()) {
return Collections.singletonList(getTaskSource().withCurrentHoursGroup(getHoursGroups()));
} else {
return synchronizationsForFromPartiallyScheduledToSchedulingPoint(schedulingState);
}
}
private List<TaskSourceSynchronization> synchronizationsForFromPartiallyScheduledToSchedulingPoint(
SchedulingDataForVersion schedulingState) {
List<TaskSourceSynchronization> result = new ArrayList<>();
for (TaskSource each : getTaskSourcesFromBottomToTop()) {
OrderElement orderElement = each.getOrderElement();
result.add(orderElement.taskSourceRemoval());
}
TaskSource newTaskSource = TaskSource.create(schedulingState, getHoursGroups());
getCurrentSchedulingData().requestedCreationOf(newTaskSource);
result.add(TaskSource.mustAdd(newTaskSource));
return result;
}
private boolean thereIsNoTaskSource() {
return getTaskSource() == null;
}
private List<OrderElement> getSomewhatScheduledOrderElements() {
List<OrderElement> result = new ArrayList<>();
for (OrderElement orderElement : getChildren()) {
if (orderElement.getSchedulingStateType().isSomewhatScheduled()) {
result.add(orderElement);
}
}
return result;
}
private List<OrderElement> getNoScheduledOrderElements() {
List<OrderElement> result = new ArrayList<>();
for (OrderElement orderElement : getChildren()) {
if (orderElement.getSchedulingState().isNoScheduled()) {
result.add(orderElement);
}
}
return result;
}
private void removeTaskSource(List<TaskSourceSynchronization> result) {
removeChildrenTaskSource(result);
if (getOnDBTaskSource() != null) {
result.add(taskSourceRemoval());
} else {
TaskSource taskSource = getTaskSource();
if (taskSource != null) {
taskSource.getTask().detach();
getCurrentSchedulingData().taskSourceRemovalRequested();
}
}
}
private TaskSource getOnDBTaskSource() {
return getCurrentVersionOnDB().getTaskSource();
}
SchedulingDataForVersion getCurrentVersionOnDB() {
return schedulingDataForVersion.get(getCurrentSchedulingData().getOriginOrderVersion());
}
private TaskSourceSynchronization taskSourceRemoval() {
Validate.notNull(getOnDBTaskSource());
TaskSourceSynchronization result = TaskSource.mustRemove(getOnDBTaskSource());
getCurrentSchedulingData().taskSourceRemovalRequested();
return result;
}
private void removeChildrenTaskSource(List<TaskSourceSynchronization> result) {
List<OrderElement> children = getChildren();
for (OrderElement each : children) {
each.removeTaskSource(result);
}
}
private boolean isSuperElementPartialOrCompletelyScheduled() {
return getSchedulingState().isSomewhatScheduled();
}
public void initializeType(SchedulingState.Type type) {
if (!isNewObject()) {
throw new IllegalStateException();
}
getCurrentSchedulingData().initializeType(type);
schedulingState = null;
}
public void initializeTemplate(OrderElementTemplate template) {
if (!isNewObject()) {
throw new IllegalStateException();
}
if (this.template != null) {
throw new IllegalStateException("already initialized");
}
this.template = template;
}
public boolean isSchedulingPoint() {
return getSchedulingState().getType() == Type.SCHEDULING_POINT;
}
public OrderLineGroup getParent() {
return parent;
}
public TaskElement getAssociatedTaskElement() {
return getTaskSource() == null ? null : getTaskSource().getTask();
}
protected void setParent(OrderLineGroup parent) {
this.parent = parent;
}
public abstract Integer getWorkHours();
public abstract List<HoursGroup> getHoursGroups();
@NotEmpty(message = "name not specified")
public String getName() {
return getInfoComponent().getName();
}
public void setName(String name) {
if ( name != null && name.length() > 255 ) {
name = name.substring(0, 255);
}
this.getInfoComponent().setName(name);
}
public Date getInitDate() {
return initDate;
}
public void setInitDate(Date initDate) {
this.initDate = initDate;
}
public Date getDeadline() {
return deadline;
}
public void setDeadline(Date deadline) {
this.deadline = deadline;
}
public void setDescription(String description) {
this.getInfoComponent().setDescription(description);
}
public String getDescription() {
return getInfoComponent().getDescription();
}
public abstract OrderLine toLeaf();
public abstract OrderLineGroup toContainer();
public boolean isScheduled() {
return getTaskSource() != null;
}
public boolean checkAtLeastOneHoursGroup() {
return !getHoursGroups().isEmpty();
}
public boolean isFormatCodeValid(String code) {
return !code.contains("_") && !"".equals(code);
}
public void setCode(String code) {
this.getInfoComponent().setCode(code);
}
public String getCode() {
return getInfoComponent().getCode();
}
public abstract OrderElementTemplate createTemplate();
public abstract DirectAdvanceAssignment getReportGlobalAdvanceAssignment();
public abstract void removeReportGlobalAdvanceAssignment();
public abstract DirectAdvanceAssignment getAdvanceAssignmentByType(AdvanceType type);
public DirectAdvanceAssignment getDirectAdvanceAssignmentByType(AdvanceType advanceType) {
if (advanceType != null) {
for (DirectAdvanceAssignment directAdvanceAssignment : getDirectAdvanceAssignments()) {
if (directAdvanceAssignment.getAdvanceType().getId().equals(advanceType.getId())) {
return directAdvanceAssignment;
}
}
}
return null;
}
public Set<DirectAdvanceAssignment> getDirectAdvanceAssignments() {
return Collections.unmodifiableSet(directAdvanceAssignments);
}
protected abstract Set<DirectAdvanceAssignment> getAllDirectAdvanceAssignments();
public abstract Set<DirectAdvanceAssignment> getAllDirectAdvanceAssignments(AdvanceType advanceType);
public abstract Set<IndirectAdvanceAssignment> getAllIndirectAdvanceAssignments(AdvanceType advanceType);
protected abstract Set<DirectAdvanceAssignment> getAllDirectAdvanceAssignmentsReportGlobal();
public void removeAdvanceAssignment(AdvanceAssignment advanceAssignment) {
if (directAdvanceAssignments.contains(advanceAssignment)) {
directAdvanceAssignments.remove(advanceAssignment);
if (this.getParent() != null) {
this.getParent().removeIndirectAdvanceAssignment(advanceAssignment.getAdvanceType());
removeChildrenAdvanceInParents(this.getParent());
}
markAsDirtyLastAdvanceMeasurementForSpreading();
updateSpreadAdvance();
}
}
public Set<Label> getLabels() {
return Collections.unmodifiableSet(labels);
}
public Set<Label> getAllLabels() {
Set<Label> allLabels = new HashSet<>();
allLabels.addAll(this.labels);
if (parent != null) {
allLabels.addAll(parent.getAllLabels());
}
return allLabels;
}
public void setLabels(Set<Label> labels) {
this.labels = labels;
}
public void addLabel(Label label) {
Validate.notNull(label);
if (!checkAncestorsNoOtherLabelRepeated(label)) {
throw new IllegalArgumentException("An ancestor has the same label assigned, " +
"so this element is already inheriting this label");
}
removeLabelOnChildren(label);
labels.add(label);
}
protected void updateLabels() {
if (parent != null) {
Set<Label> toRemove = new HashSet<>();
for (Label each : labels) {
if (!parent.checkAncestorsNoOtherLabelRepeated(each)) {
toRemove.add(each);
}
}
labels.removeAll(toRemove);
}
for (OrderElement each : getChildren()) {
each.updateLabels();
}
}
public void removeLabel(Label label) {
labels.remove(label);
}
/**
* Validate if the advanceAssignment can be added to the order element.
* The list of advanceAssignments must be attached.
*
* @param newAdvanceAssignment
* must be attached
* @throws DuplicateValueTrueReportGlobalAdvanceException
* @throws DuplicateAdvanceAssignmentForOrderElementException
*/
public void addAdvanceAssignment(DirectAdvanceAssignment newAdvanceAssignment)
throws DuplicateValueTrueReportGlobalAdvanceException, DuplicateAdvanceAssignmentForOrderElementException {
checkNoOtherGlobalAdvanceAssignment(newAdvanceAssignment);
checkAncestorsNoOtherAssignmentWithSameAdvanceType(this, newAdvanceAssignment);
checkChildrenNoOtherAssignmentWithSameAdvanceType(this, newAdvanceAssignment);
if ( getReportGlobalAdvanceAssignment() == null ) {
newAdvanceAssignment.setReportGlobalAdvance(true);
}
newAdvanceAssignment.setOrderElement(this);
this.directAdvanceAssignments.add(newAdvanceAssignment);
if ( this.getParent() != null ) {
addChildrenAdvanceInParents(this.getParent());
this.getParent()
.addIndirectAdvanceAssignment(newAdvanceAssignment.createIndirectAdvanceFor(this.getParent()));
}
}
public void addChildrenAdvanceInParents(OrderLineGroup parent) {
if ( (parent != null) && (!parent.existChildrenAdvance()) ) {
parent.addChildrenAdvanceOrderLineGroup();
addChildrenAdvanceInParents(parent.getParent());
}
}
public void removeChildrenAdvanceInParents(OrderLineGroup parent) {
if ( (parent != null) && (parent.existChildrenAdvance() ) && (!itsChildrenHasAdvances(parent))) {
parent.removeChildrenAdvanceOrderLineGroup();
removeChildrenAdvanceInParents(parent.getParent());
}
}
private boolean itsChildrenHasAdvances(OrderElement orderElement) {
for (OrderElement child : orderElement.getChildren()) {
if ( (!child.getIndirectAdvanceAssignments().isEmpty()) ||
(!child.getDirectAdvanceAssignments().isEmpty()) ) {
return true;
}
if ( itsChildrenHasAdvances(child) ) {
return true;
}
}
return false;
}
protected void checkNoOtherGlobalAdvanceAssignment(DirectAdvanceAssignment newAdvanceAssignment)
throws DuplicateValueTrueReportGlobalAdvanceException {
if ( !newAdvanceAssignment.getReportGlobalAdvance() ) {
return;
}
for (DirectAdvanceAssignment directAdvanceAssignment : directAdvanceAssignments) {
if ( directAdvanceAssignment.getReportGlobalAdvance() ) {
throw new DuplicateValueTrueReportGlobalAdvanceException(
_("Cannot spread two progress in the same task"), this, OrderElement.class);
}
}
}
/**
* It checks there are no {@link DirectAdvanceAssignment} with the same type in {@link OrderElement} and ancestors.
*
* @param orderElement
* @param newAdvanceAssignment
* @throws DuplicateAdvanceAssignmentForOrderElementException
*/
public void checkAncestorsNoOtherAssignmentWithSameAdvanceType(
OrderElement orderElement, DirectAdvanceAssignment newAdvanceAssignment)
throws DuplicateAdvanceAssignmentForOrderElementException {
for (DirectAdvanceAssignment directAdvanceAssignment : orderElement.getDirectAdvanceAssignments()) {
if ( AdvanceType.equivalentInDB(
directAdvanceAssignment.getAdvanceType(), newAdvanceAssignment.getAdvanceType()) ) {
throw new DuplicateAdvanceAssignmentForOrderElementException(
_("Duplicate Progress Assignment For Task"), this, OrderElement.class);
}
}
if (orderElement.getParent() != null) {
checkAncestorsNoOtherAssignmentWithSameAdvanceType(orderElement.getParent(), newAdvanceAssignment);
}
}
/**
* It checks there are no {@link AdvanceAssignment} with the same type in orderElement and its children.
*
* @param orderElement
* @param newAdvanceAssignment
* @throws DuplicateAdvanceAssignmentForOrderElementException
*/
protected void checkChildrenNoOtherAssignmentWithSameAdvanceType(
OrderElement orderElement, DirectAdvanceAssignment newAdvanceAssignment)
throws DuplicateAdvanceAssignmentForOrderElementException {
if (orderElement.existsDirectAdvanceAssignmentWithTheSameType(newAdvanceAssignment.getAdvanceType())) {
throw new DuplicateAdvanceAssignmentForOrderElementException(
_("Duplicate Progress Assignment For Task"),
this,
OrderElement.class);
}
if (!orderElement.getChildren().isEmpty()) {
for (OrderElement child : orderElement.getChildren()) {
checkChildrenNoOtherAssignmentWithSameAdvanceType(child, newAdvanceAssignment);
}
}
}
public boolean existsDirectAdvanceAssignmentWithTheSameType(AdvanceType type) {
for (DirectAdvanceAssignment directAdvanceAssignment : directAdvanceAssignments) {
if (AdvanceType.equivalentInDB(directAdvanceAssignment.getAdvanceType(), type)) {
return true;
}
}
return false;
}
public BigDecimal getAdvancePercentage() {
if ((dirtyLastAdvanceMeasurementForSpreading == null) || dirtyLastAdvanceMeasurementForSpreading) {
lastAdvanceMeasurementForSpreading = getAdvancePercentage(null);
dirtyLastAdvanceMeasurementForSpreading = false;
}
return lastAdvanceMeasurementForSpreading;
}
public abstract BigDecimal getAdvancePercentage(LocalDate date);
public abstract Set<IndirectAdvanceAssignment> getIndirectAdvanceAssignments();
public abstract DirectAdvanceAssignment calculateFakeDirectAdvanceAssignment(
IndirectAdvanceAssignment indirectAdvanceAssignment);
public abstract BigDecimal getAdvancePercentageChildren();
public List<OrderElement> getAllChildren() {
List<OrderElement> children = getChildren();
List<OrderElement> result = new ArrayList<>();
for (OrderElement orderElement : children) {
result.add(orderElement);
result.addAll(orderElement.getAllChildren());
}
return result;
}
public void setCriterionRequirements(Set<CriterionRequirement> criterionRequirements) {
this.criterionRequirements = criterionRequirements;
}
@Valid
@Override
public Set<CriterionRequirement> getCriterionRequirements() {
return Collections.unmodifiableSet(criterionRequirements);
}
/**
* Operations to manage the criterion requirements of a orderElement
* (remove, adding, update of the criterion requirement of the orderElement such as the descendant's criterion requirement)
*/
public void setValidCriterionRequirement(IndirectCriterionRequirement requirement,boolean valid) {
requirement.setValid(valid);
criterionRequirementHandler.propagateValidCriterionRequirement(this, requirement.getParent(), valid);
}
public void removeDirectCriterionRequirement(DirectCriterionRequirement criterionRequirement) {
criterionRequirementHandler.propagateRemoveCriterionRequirement(this, criterionRequirement);
removeCriterionRequirement(criterionRequirement);
}
@Override
public void removeCriterionRequirement(CriterionRequirement requirement) {
criterionRequirements.remove(requirement);
if (requirement instanceof IndirectCriterionRequirement) {
((IndirectCriterionRequirement)requirement).getParent().getChildren().remove(requirement);
}
}
@Override
public void addCriterionRequirement(CriterionRequirement criterionRequirement) {
criterionRequirementHandler.addCriterionRequirement(this, criterionRequirement);
}
public void addDirectCriterionRequirement(CriterionRequirement criterionRequirement) {
criterionRequirementHandler.addDirectCriterionRequirement(this, criterionRequirement);
}
public void addIndirectCriterionRequirement(IndirectCriterionRequirement criterionRequirement) {
criterionRequirementHandler.addIndirectCriterionRequirement(this, criterionRequirement);
}
protected void basicAddCriterionRequirement(CriterionRequirement criterionRequirement) {
criterionRequirement.setOrderElement(this);
this.criterionRequirements.add(criterionRequirement);
}
public void updateCriterionRequirements() {
criterionRequirementHandler.updateMyCriterionRequirements(this);
criterionRequirementHandler.propagateUpdateCriterionRequirements(this);
}
public boolean canAddCriterionRequirement(DirectCriterionRequirement newRequirement) {
return criterionRequirementHandler.canAddCriterionRequirement(this, newRequirement);
}
public Set<IndirectCriterionRequirement> getIndirectCriterionRequirement() {
return criterionRequirementHandler.getIndirectCriterionRequirement(criterionRequirements);
}
public void updatePositionConstraintOf(Task task) {
applyConstraintsInOrderElementParents(task);
}
public void applyInitialPositionConstraintTo(Task task) {
boolean applied = applyConstraintsInOrderElementParents(task);
if (applied) {
return;
}
if (getOrder().isScheduleBackwards()) {
task.getPositionConstraint().asLateAsPossible();
} else {
task.getPositionConstraint().asSoonAsPossible();
}
}
/**
* Searches for init date or end constraints on order element and order element parents.
*
* @param task
* @return <code>true</code> if a constraint have been applied, otherwise
* <code>false</code>
*/
private boolean applyConstraintsInOrderElementParents(Task task) {
boolean scheduleBackwards = getOrder().isScheduleBackwards();
OrderElement current = this;
while (current != null) {
boolean applied = current.applyConstraintBasedOnInitOrEndDate(task, scheduleBackwards);
if (applied) {
return true;
}
current = current.getParent();
}
return false;
}
protected boolean applyConstraintBasedOnInitOrEndDate(Task task, boolean scheduleBackwards) {
TaskPositionConstraint constraint = task.getPositionConstraint();
if (getInitDate() != null && (getDeadline() == null || !scheduleBackwards)) {
constraint.notEarlierThan(IntraDayDate.startOfDay(LocalDate.fromDateFields(this.getInitDate())));
return true;
}
return false;
}
public Set<DirectCriterionRequirement> getDirectCriterionRequirement() {
return criterionRequirementHandler.getDirectCriterionRequirement(criterionRequirements);
}
public SchedulingState.Type getSchedulingStateType() {
return getCurrentSchedulingData().getSchedulingStateType();
}
public TaskSource getTaskSource() {
return getCurrentSchedulingData().getTaskSource();
}
public TaskElement getTaskElement() {
TaskSource taskSource = getTaskSource();
return taskSource == null ? null : taskSource.getTask();
}
public Set<TaskElement> getTaskElements() {
return getTaskSource() == null
? Collections.emptySet()
: Collections.singleton(getTaskSource().getTask());
}
public List<TaskSource> getTaskSourcesFromBottomToTop() {
List<TaskSource> result = new ArrayList<>();
taskSourcesFromBottomToTop(result);
return result;
}
public List<TaskSource> getAllScenariosTaskSourcesFromBottomToTop() {
List<TaskSource> result = new ArrayList<>();
allScenariosTaskSourcesFromBottomToTop(result);
return result;
}
public List<SchedulingDataForVersion> getSchedulingDataForVersionFromBottomToTop() {
List<SchedulingDataForVersion> result = new ArrayList<>();
schedulingDataForVersionFromBottomToTop(result);
return result;
}
private void schedulingDataForVersionFromBottomToTop(List<SchedulingDataForVersion> result) {
for (OrderElement each : getChildren()) {
each.schedulingDataForVersionFromBottomToTop(result);
}
result.addAll(schedulingDataForVersion.values());
}
private void taskSourcesFromBottomToTop(List<TaskSource> result) {
for (OrderElement each : getChildren()) {
each.taskSourcesFromBottomToTop(result);
}
if ( getTaskSource() != null ) {
result.add(getTaskSource());
}
}
private void allScenariosTaskSourcesFromBottomToTop(List<TaskSource> result) {
for (OrderElement each : getChildren()) {
each.allScenariosTaskSourcesFromBottomToTop(result);
}
for (Entry<OrderVersion, SchedulingDataForVersion> each : schedulingDataForVersion.entrySet()) {
TaskSource taskSource = each.getValue().getTaskSource();
if (taskSource != null) {
result.add(taskSource);
}
}
}
@Valid
public Set<MaterialAssignment> getMaterialAssignments() {
return Collections.unmodifiableSet(materialAssignments);
}
public void addMaterialAssignment(MaterialAssignment materialAssignment) {
materialAssignments.add(materialAssignment);
materialAssignment.setOrderElement(this);
}
public void removeMaterialAssignment(MaterialAssignment materialAssignment) {
materialAssignments.remove(materialAssignment);
}
public BigDecimal getTotalMaterialAssignmentUnits() {
BigDecimal result = BigDecimal.ZERO;
final Set<MaterialAssignment> materialAssignments = getMaterialAssignments();
for (MaterialAssignment each: materialAssignments) {
result = result.add(each.getUnits());
}
return result;
}
public BigDecimal getTotalMaterialAssignmentPrice() {
BigDecimal result = new BigDecimal(0);
final Set<MaterialAssignment> materialAssignments = getMaterialAssignments();
for (MaterialAssignment each: materialAssignments) {
result = result.add(each.getTotalPrice());
}
return result;
}
public Order getOrder() {
return parent == null ? null : parent.getOrder();
}
@Valid
public Set<TaskQualityForm> getTaskQualityForms() {
return Collections.unmodifiableSet(taskQualityForms);
}
public Set<QualityForm> getQualityForms() {
Set<QualityForm> result = new HashSet<>();
for (TaskQualityForm each : taskQualityForms) {
result.add(each.getQualityForm());
}
return result;
}
public void setTaskQualityFormItems(Set<TaskQualityForm> taskQualityForms) {
this.taskQualityForms = taskQualityForms;
}
public TaskQualityForm addTaskQualityForm(QualityForm qualityForm) throws ValidationException {
checkUniqueQualityForm(qualityForm);
TaskQualityForm taskQualityForm = TaskQualityForm.create(this, qualityForm);
this.taskQualityForms.add(taskQualityForm);
return taskQualityForm;
}
public void removeTaskQualityForm(TaskQualityForm taskQualityForm) {
this.taskQualityForms.remove(taskQualityForm);
}
private void checkUniqueQualityForm(QualityForm qualityForm) throws ValidationException, IllegalArgumentException {
Validate.notNull(qualityForm);
for (TaskQualityForm taskQualityForm : getTaskQualityForms()) {
if ( qualityForm.equals(taskQualityForm.getQualityForm()) ) {
throw new ValidationException(ValidationException.invalidValue(
_("Quality form already exists"),
"name",
qualityForm.getName(),
qualityForm));
}
}
}
@Override
public boolean isUniqueCodeConstraint() {
// The automatic checking of this constraint is avoided because it uses the wrong code property
return true;
}
@AssertTrue(message = "code is already used in another project")
public boolean isCodeRepeatedInAnotherOrderConstraint() {
return StringUtils.isBlank(getCode()) ||
!Registry.getOrderElementDAO().existsByCodeInAnotherOrderAnotherTransaction(this);
}
@AssertTrue(message = "a label can not be assigned twice in the same branch")
public boolean isLabelNotRepeatedInTheSameBranchConstraint() {
return checkConstraintLabelNotRepeatedInTheSameBranch(new HashSet<>());
}
private boolean checkConstraintLabelNotRepeatedInTheSameBranch(HashSet<Label> parentLabels) {
HashSet<Label> withThisLabels = new HashSet<>(parentLabels);
for (Label label : getLabels()) {
if (containsLabel(withThisLabels, label)) {
return false;
}
withThisLabels.add(label);
}
for (OrderElement child : getChildren()) {
if (!child.checkConstraintLabelNotRepeatedInTheSameBranch(withThisLabels)) {
return false;
}
}
return true;
}
private boolean containsLabel(HashSet<Label> labels, Label label) {
for (Label each : labels) {
if (each.isEqualTo(label)) {
return true;
}
}
return false;
}
public boolean checkAncestorsNoOtherLabelRepeated(Label newLabel) {
for (Label label : labels) {
if (label.isEqualTo(newLabel)) {
return false;
}
}
return !(parent != null && !parent.checkAncestorsNoOtherLabelRepeated(newLabel));
}
private void removeLabelOnChildren(Label newLabel) {
Label toRemove = null;
for (Label label : labels) {
if (label.equals(newLabel)) {
toRemove = label;
break;
}
}
if (toRemove != null) {
removeLabel(toRemove);
}
for (OrderElement child : getChildren()) {
child.removeLabelOnChildren(newLabel);
}
}
public boolean containsOrderElement(String code) {
for (OrderElement child : getChildren()) {
if (child.getCode().equals(code)) {
return true;
}
}
return false;
}
public OrderElement getOrderElement(String code) {
if (code == null) {
return null;
}
for (OrderElement child : getChildren()) {
if (child.getCode().equals(code)) {
return child;
}
}
return null;
}
public boolean containsLabel(String code) {
for (Label label : getLabels()) {
if (label.getCode().equals(code)) {
return true;
}
}
return false;
}
public boolean containsLabels(Set<Label> labels) {
Integer matches = 0;
for (Label label : labels) {
if (containsLabel(label.getCode())) {
matches++;
}
}
return matches == labels.size();
}
public boolean containsCriterion(String code) {
for (CriterionRequirement criterionRequirement : getDirectCriterionRequirement()) {
if (criterionRequirement.getCriterion().getCode().equals(code)) {
return true;
}
}
return false;
}
public boolean containsCriteria(Set<Criterion> criteria) {
Integer matches = 0;
for (Criterion criterion : criteria) {
if (containsCriterion(criterion.getCode())) {
matches++;
}
}
return matches == criteria.size();
}
public boolean containsMaterialAssignment(String materialCode) {
for (MaterialAssignment materialAssignment : getMaterialAssignments()) {
if (materialAssignment.getMaterial().getCode().equals(materialCode)) {
return true;
}
}
return false;
}
public MaterialAssignment getMaterialAssignment(String materialCode) {
for (MaterialAssignment materialAssignment : getMaterialAssignments()) {
if (materialAssignment.getMaterial().getCode().equals(materialCode)) {
return materialAssignment;
}
}
return null;
}
public DirectAdvanceAssignment getDirectAdvanceAssignmentSubcontractor() {
for (DirectAdvanceAssignment directAdvanceAssignment : directAdvanceAssignments) {
if (directAdvanceAssignment.getAdvanceType().getUnitName()
.equals(PredefinedAdvancedTypes.SUBCONTRACTOR.getTypeName())) {
return directAdvanceAssignment;
}
}
return null;
}
public DirectAdvanceAssignment addSubcontractorAdvanceAssignment()
throws DuplicateValueTrueReportGlobalAdvanceException, DuplicateAdvanceAssignmentForOrderElementException {
boolean reportGlobalAdvance = false;
if (getReportGlobalAdvanceAssignment() == null) {
reportGlobalAdvance = true;
}
DirectAdvanceAssignment directAdvanceAssignment =
DirectAdvanceAssignment.create(reportGlobalAdvance, new BigDecimal(100));
directAdvanceAssignment.setAdvanceType(PredefinedAdvancedTypes.SUBCONTRACTOR.getType());
addAdvanceAssignment(directAdvanceAssignment);
return directAdvanceAssignment;
}
public InfoComponentWithCode getInfoComponent() {
if (infoComponent == null) {
infoComponent = new InfoComponentWithCode();
}
return infoComponent;
}
@Override
public OrderElement getThis() {
return this;
}
public void setExternalCode(String externalCode) {
this.externalCode = externalCode;
}
public String getExternalCode() {
return externalCode;
}
public abstract OrderLine calculateOrderLineForSubcontract();
public Set<MaterialAssignment> getAllMaterialAssignments() {
Set<MaterialAssignment> result = new HashSet<>();
result.addAll(getMaterialAssignments());
for (OrderElement orderElement : getChildren()) {
result.addAll(orderElement.getAllMaterialAssignments());
}
return result;
}
/**
* Calculate if the tasks of the planification point has finished.
*/
public boolean isFinishPlanificationPointTask() {
// Look up into the order elements tree
TaskElement task = lookToUpAssignedTask();
if (task != null) {
return task.getOrderElement().isFinishedAdvance();
}
// Look down into the order elements tree
List<TaskElement> listTask = lookToDownAssignedTask();
if (!listTask.isEmpty()) {
for (TaskElement taskElement : listTask) {
if (!taskElement.getOrderElement().isFinishedAdvance()) {
return false;
}
}
}
// Not exist assigned task
return (Registry.getOrderDAO().loadOrderAvoidingProxyFor(this)).isFinishedAdvance();
}
private TaskElement lookToUpAssignedTask() {
OrderElement current = this;
while (current != null) {
if (isSchedulingPoint()) {
return getAssociatedTaskElement();
}
current = current.getParent();
}
return null;
}
private List<TaskElement> lookToDownAssignedTask() {
List<TaskElement> resultTask = new ArrayList<>();
for (OrderElement child : getAllChildren()) {
if (child.isSchedulingPoint()) {
TaskElement task = child.getAssociatedTaskElement();
if (task != null) {
resultTask.add(task);
}
}
}
return resultTask;
}
public boolean isFinishedAdvance() {
BigDecimal measuredProgress = getAdvancePercentage();
measuredProgress = measuredProgress.setScale(0, BigDecimal.ROUND_UP).multiply(new BigDecimal(100));
return measuredProgress.compareTo(new BigDecimal(100)) == 0;
}
@Override
protected IIntegrationEntityDAO<OrderElement> getIntegrationEntityDAO() {
return Registry.getOrderElementDAO();
}
public void markAsDirtyLastAdvanceMeasurementForSpreading() {
if (parent != null) {
parent.markAsDirtyLastAdvanceMeasurementForSpreading();
}
dirtyLastAdvanceMeasurementForSpreading = true;
}
public void setSumChargedEffort(SumChargedEffort sumChargedHours) {
this.sumChargedEffort = sumChargedHours;
}
public SumChargedEffort getSumChargedEffort() {
return sumChargedEffort;
}
public void updateAdvancePercentageTaskElement() {
BigDecimal advancePercentage = this.getAdvancePercentage();
if (this.getTaskSource() != null && this.getTaskSource().getTask() != null) {
this.getTaskSource().getTask().setAdvancePercentage(advancePercentage);
}
if (parent != null) {
parent.updateAdvancePercentageTaskElement();
}
}
public void setCodeAutogenerated(Boolean codeAutogenerated) {
if ( getOrder().equals(this) ) {
super.setCodeAutogenerated(codeAutogenerated);
}
}
public Boolean isCodeAutogenerated() {
if ( getOrder().equals(this) ) {
return super.isCodeAutogenerated();
}
return (getOrder() != null) ? getOrder().isCodeAutogenerated() : false;
}
@AssertTrue(message = "a quality form cannot be assigned twice to the same task")
public boolean isUniqueQualityFormConstraint() {
Set<QualityForm> qualityForms = new HashSet<>();
for (TaskQualityForm each : taskQualityForms) {
QualityForm qualityForm = each.getQualityForm();
if ( qualityForms.contains(qualityForm) ) {
return false;
}
qualityForms.add(qualityForm);
}
return true;
}
public void removeDirectAdvancesInList(Set<DirectAdvanceAssignment> directAdvanceAssignments) {
for (DirectAdvanceAssignment each : directAdvanceAssignments) {
removeAdvanceAssignment(getAdvanceAssignmentByType(each.getAdvanceType()));
}
for (OrderElement each : getChildren()) {
each.removeDirectAdvancesInList(directAdvanceAssignments);
}
}
protected Set<DirectAdvanceAssignment> getDirectAdvanceAssignmentsAndAllInAncest() {
Set<DirectAdvanceAssignment> result = new HashSet<>();
result.addAll(directAdvanceAssignments);
if ( getParent() != null ) {
result.addAll(getParent().getDirectAdvanceAssignmentsAndAllInAncest());
}
return result;
}
protected void updateSpreadAdvance() {
if ( getReportGlobalAdvanceAssignment() == null ) {
// Set PERCENTAGE type as spread if any
String type = PredefinedAdvancedTypes.PERCENTAGE.getTypeName();
for (DirectAdvanceAssignment each : directAdvanceAssignments) {
if ( each.getAdvanceType() != null &&
each.getAdvanceType().getType() != null &&
each.getAdvanceType().getType().equals(type) ) {
each.setReportGlobalAdvance(true);
return;
}
}
// Otherwise, set first advance assignment
if ( !directAdvanceAssignments.isEmpty() ) {
directAdvanceAssignments.iterator().next().setReportGlobalAdvance(true);
return;
}
}
}
public List<OrderVersion> getOrderVersions() {
return new ArrayList<>(schedulingDataForVersion.keySet());
}
@Override
public String toString() {
return super.toString() + " :: " + getName();
}
public List<WorkReportLine> getWorkReportLines(boolean sortedByDate) {
return Registry.getWorkReportLineDAO().findByOrderElementAndChildren(this, sortedByDate);
}
/**
* Gets workReportLines of this order-element between the specified
* <code>startDate</code> and <code>endDate</code>.
*
* @param startDate
* the startDate
* @param endDate
* the endDate
* @param sortedByDate
* @return list of workReportLines
*/
public List<WorkReportLine> getWorkReportLines(Date startDate, Date endDate, boolean sortedByDate) {
return Registry
.getWorkReportLineDAO()
.findByOrderElementAndChildrenFilteredByDate(this, startDate, endDate, sortedByDate);
}
/**
* Checks if it has nay consolidated advance, if not checks if any parent has it.
*/
public boolean hasAnyConsolidatedAdvance() {
for (DirectAdvanceAssignment each : directAdvanceAssignments) {
if (each.hasAnyConsolidationValue()) {
return true;
}
}
for (IndirectAdvanceAssignment each : getIndirectAdvanceAssignments()) {
if (each.hasAnyConsolidationValue()) {
return true;
}
}
if (parent != null) {
return parent.hasAnyConsolidatedAdvance();
}
return false;
}
public abstract BigDecimal getBudget();
public void setSumExpenses(SumExpenses sumExpenses) {
this.sumExpenses = sumExpenses;
}
public SumExpenses getSumExpenses() {
return this.sumExpenses;
}
public boolean isOrder() {
return false;
}
public boolean hasTimesheetsReportingHours() {
return sumChargedEffort != null && sumChargedEffort.getFirstTimesheetDate() != null;
}
public boolean isFinishedTimesheets() {
return sumChargedEffort == null ? false : sumChargedEffort.isFinishedTimesheets();
}
@Override
public boolean isUpdatedFromTimesheets() {
TaskElement taskElement = getTaskElement();
return taskElement == null ? false : taskElement.isUpdatedFromTimesheets();
}
public Date getFirstTimesheetDate() {
return sumChargedEffort == null ? null : sumChargedEffort.getFirstTimesheetDate();
}
public Date getLastTimesheetDate() {
return sumChargedEffort == null ? null : sumChargedEffort.getLastTimesheetDate();
}
public void detachFromParent() {
parent = null;
}
public AdvanceMeasurement getLastAdvanceMeasurement() {
DirectAdvanceAssignment advanceAssignment = getReportGlobalAdvanceAssignment();
return advanceAssignment == null ? null : advanceAssignment.getLastAdvanceMeasurement();
}
public String getEffortAsString() {
SumChargedEffort sumChargedEffort = getSumChargedEffort();
EffortDuration effort = sumChargedEffort != null
? sumChargedEffort.getTotalChargedEffort()
: EffortDuration.zero();
return effort.toFormattedString();
}
public boolean isJiraIssue() {
String code = getCode();
return code == null
? false
: code.startsWith(PredefinedConnectorProperties.JIRA_CODE_PREFIX);
}
public boolean isConvertedToContainer() {
return false;
}
public BigDecimal getTotalBudget() {
return getBudget().add(getResourcesBudget());
}
public BigDecimal getSubstractedBudget() {
return getBudget().subtract(getResourcesBudget());
}
public BigDecimal getResourcesBudget() {
return Registry.getTransactionService().runOnReadOnlyTransaction(
() -> calculateBudgetFromCriteriaAndCostCategories());
}
public BigDecimal calculateBudgetFromCriteriaAndCostCategories() {
BigDecimal totalBudget = new BigDecimal(0);
Configuration configuration = Registry.getConfigurationDAO().getConfiguration();
TypeOfWorkHours typeofWorkHours = configuration.getBudgetDefaultTypeOfWorkHours();
if (!configuration.isEnabledAutomaticBudget() || (configuration.getBudgetDefaultTypeOfWorkHours() == null)) {
return totalBudget;
}
BigDecimal costPerHour = new BigDecimal(0);
BigDecimal hours;
for (HoursGroup hoursGroup : getHoursGroups()) {
hours = new BigDecimal(hoursGroup.getWorkingHours());
for (CriterionRequirement crit : hoursGroup.getCriterionRequirements()) {
CostCategory costcat = crit.getCriterion().getCostCategory();
if (costcat != null) {
IHourCostDAO hourCostDAO = Registry.getHourCostDAO();
costPerHour = hourCostDAO.getPriceCostFromCriterionAndType(costcat, typeofWorkHours);
}
totalBudget = totalBudget.add(costPerHour.multiply(hours));
}
if (hoursGroup.getCriterionRequirements().size() > 1) {
totalBudget = totalBudget.divide(new BigDecimal(hoursGroup.getCriterionRequirements().size()));
}
}
return totalBudget;
}
/**
* Returns with margin calculated hours for this orderElement.
*/
public EffortDuration getWithMarginCalculatedHours() {
return calculateWorkHoursWithMargin();
}
/**
* Calculates the work hours with the margin {@link Order#getHoursMargin()} for this orderElement.
*
* @return calculated work hours
*/
private EffortDuration calculateWorkHoursWithMargin() {
BigDecimal margin =
this.getOrder().getHoursMargin() != null
? new BigDecimal(this.getOrder().getHoursMargin()).setScale(2)
: BigDecimal.ZERO;
BigDecimal hundred = new BigDecimal(100);
BigDecimal estimatedHours = new BigDecimal(getWorkHours()).setScale(2);
BigDecimal marginHours = estimatedHours.multiply(margin).divide(hundred, 2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal result = estimatedHours.add(marginHours);
return EffortDuration.fromHoursAsBigDecimal(result);
}
/**
* Returns with margin calculated budget for this orderElement.
*/
public BigDecimal getWithMarginCalculatedBudget() {
return calculateBudgetWithMargin();
}
/**
* Calculates the budget with the margin {@link Order#getBudgetMargin()} for this orderElement.
*
* @return calculated budget
*/
private BigDecimal calculateBudgetWithMargin() {
BigDecimal margin =
this.getOrder().getBudgetMargin() != null
? new BigDecimal(this.getOrder().getBudgetMargin())
: BigDecimal.ZERO;
BigDecimal hundred = new BigDecimal(100);
BigDecimal budget = getBudget();
BigDecimal marginBudget = budget.multiply(margin).divide(hundred, 2, BigDecimal.ROUND_HALF_EVEN);
return budget.add(marginBudget);
}
}