/* * 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.web.planner.allocation; import static org.libreplan.business.workingday.EffortDuration.hours; import static org.libreplan.business.workingday.EffortDuration.zero; import static org.libreplan.web.I18nHelper._; import static org.libreplan.web.planner.allocation.AllocationRow.assignEfforts; import static org.libreplan.web.planner.allocation.AllocationRow.sumAllEffortFromInputs; import static org.libreplan.web.planner.allocation.AllocationRow.sumAllOriginalEffort; import static org.libreplan.web.planner.allocation.AllocationRow.sumAllTotalEffort; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.libreplan.business.common.Flagged; import org.libreplan.business.common.ProportionalDistributor; import org.libreplan.business.planner.entities.AggregateOfResourceAllocations; import org.libreplan.business.planner.entities.CalculatedValue; import org.libreplan.business.planner.entities.Task; import org.libreplan.business.resources.entities.Criterion; import org.libreplan.business.resources.entities.Resource; import org.libreplan.business.resources.entities.ResourceEnum; import org.libreplan.business.scenarios.entities.Scenario; import org.libreplan.business.workingday.EffortDuration; import org.libreplan.business.workingday.IntraDayDate; import org.libreplan.business.workingday.ResourcesPerDay; import org.libreplan.web.common.EffortDurationBox; import org.libreplan.web.common.IMessagesForUser; import org.libreplan.web.common.Level; import org.libreplan.web.common.Util; import org.libreplan.web.common.components.NewAllocationSelectorCombo; import org.libreplan.web.common.components.ResourceAllocationBehaviour; import org.libreplan.web.planner.allocation.AllocationRowsHandler.Warnings; import org.libreplan.web.planner.allocation.IResourceAllocationModel.IResourceAllocationContext; import org.libreplan.web.planner.taskedition.TaskPropertiesController; import org.zkoss.util.Locales; import org.zkoss.zk.au.out.AuWrongValue; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.Button; import org.zkoss.zul.Constraint; import org.zkoss.zul.Decimalbox; import org.zkoss.zul.Grid; import org.zkoss.zul.Intbox; import org.zkoss.zul.Label; import org.zkoss.zul.SimpleConstraint; import org.zkoss.zul.Tab; import org.zkoss.zul.impl.InputElement; public class FormBinder { private EffortDurationBox effortInput; private Label allOriginalEffort; private Label allTotalEffort; private Label allConsolidatedEffort; private Label allTotalResourcesPerDay; private Label allConsolidatedResourcesPerDay; private final AllocationRowsHandler allocationRowsHandler; private AggregateOfResourceAllocations aggregate; private AllocationResult lastAllocation; private Button applyButton; private EventListener onChangeEnableApply = new EventListener() { @Override public void onEvent(Event event) { Component target = event.getTarget(); if ( target instanceof InputElement ) { InputElement inputElement = (InputElement) target; if ( inputElement.isDisabled() ) { return; } } Clients.clearWrongValue(allocationsGrid); applyButton.setDisabled(false); } }; private ResourceAllocationBehaviour behaviour; private Grid allocationsGrid; private EventListener applyButtonListener; private List<InputElement> inputsAssociatedWithOnChangeEnableApply = new ArrayList<>(); private IMessagesForUser messagesForUser; private final IResourceAllocationModel resourceAllocationModel; private List<AllocationRow> rows = Collections.emptyList(); private WorkableDaysAndDatesBinder workableDaysAndDatesBinder; private Button btnRecommendedAllocation; private ProportionalDistributor hoursDistributorForRecommendedAllocation; private ResourcesPerDay.ResourcesPerDayDistributor resourcesPerDayDistributorForRecommendedAllocation; private EventListener hoursRowInputChange = new EventListener() { @Override public void onEvent(Event event) { if ( effortInput.isDisabled() ) { effortInput.setValue(sumAllEffortFromInputs(rows)); } } }; private EventListener resourcesPerDayRowInputChange = new EventListener() { @Override public void onEvent(Event event) { if ( allResourcesPerDay.isDisabled() ) { sumResourcesPerDayFromRowsAndAssignToAllResourcesPerDay(); } } }; private EventListener allHoursInputChange = new EventListener() { @Override public void onEvent(Event event) { if ( !effortInput.isDisabled() ) { distributeHoursFromTotalToRows(); } } }; private EventListener allResourcesPerDayChange = new EventListener() { @Override public void onEvent(Event event) { if ( !allResourcesPerDay.isDisabled() ) { distributeResourcesPerDayToRows(); } } }; private boolean recommendedAllocation = false; private NewAllocationSelectorCombo newAllocationSelectorCombo; private Tab workerSearchTab; private Decimalbox allResourcesPerDay; private Button advancedSearchButton; public FormBinder(Scenario currentScenario, AllocationRowsHandler allocationRowsHandler, IResourceAllocationModel resourceAllocationModel) { this.allocationRowsHandler = allocationRowsHandler; this.resourceAllocationModel = resourceAllocationModel; this.lastAllocation = this.allocationRowsHandler.getInitialAllocation(currentScenario); this.aggregate = this.lastAllocation.getAggregate(); } void setAssignedEffortComponent(EffortDurationBox assignedEffortComponent) { this.effortInput = assignedEffortComponent; this.effortInput.setConstraint(positiveValueRequired()); allHoursInputComponentDisabilityRule(); loadValueForEffortInput(); onChangeEnableApply(assignedEffortComponent); } private void loadValueForEffortInput() { this.effortInput.setValue(aggregate.isEmpty() ? hours(allocationRowsHandler.getTask().getWorkHours()) : aggregate.getTotalEffort()); } private void allHoursInputComponentDisabilityRule() { CalculatedValue c = allocationRowsHandler.getCalculatedValue(); boolean disabled = rows.isEmpty() || (CalculatedValue.NUMBER_OF_HOURS == c) || (c == CalculatedValue.RESOURCES_PER_DAY && !recommendedAllocation) || isAnyManual() || isTaskUpdatedFromTimesheets(); this.effortInput.setDisabled(disabled); } public AllocationResult getLastAllocation() { return lastAllocation; } void setCalculatedValue(CalculatedValue newCalculatedValue) { CalculatedValue previousCalculatedValue = allocationRowsHandler.getCalculatedValue(); if (newCalculatedValue == previousCalculatedValue) { return; } allocationRowsHandler.setCalculatedValue(newCalculatedValue); applyDisabledRules(); sumResourcesPerDayFromRowsAndAssignToAllResourcesPerDay(); workableDaysAndDatesBinder.switchFromTo(previousCalculatedValue, newCalculatedValue); applyButton.setDisabled(false); } private void applyDisabledRules() { allHoursInputComponentDisabilityRule(); workableDaysAndDatesBinder.applyDisabledRules(); allResourcesPerDayVisibilityRule(); applyDisabledRulesOnRows(); this.btnRecommendedAllocation.setDisabled(recommendedAllocation || isAnyManual() || isTaskUpdatedFromTimesheets()); } private void applyDisabledRulesOnRows() { for (AllocationRow each : rows) { each.applyDisabledRules(getCalculatedValue(), recommendedAllocation, isAnyManual() || isTaskUpdatedFromTimesheets()); } } private void bindTotalHoursToHoursInputs() { for (AllocationRow each : rows) { each.addListenerForHoursInputChange(hoursRowInputChange); } effortInput.setValue(sumAllEffortFromInputs(this.rows)); } CalculatedValue getCalculatedValue() { return allocationRowsHandler.getCalculatedValue(); } private void onChangeEnableApply(InputElement inputElement) { Util.ensureUniqueListener(inputElement, Events.ON_CHANGE, onChangeEnableApply); } void setWorkableDays(Intbox duration, final TaskPropertiesController taskPropertiesController, final Label labelTaskStart, final Label labelTaskEnd) { this.workableDaysAndDatesBinder = new WorkableDaysAndDatesBinder(duration, labelTaskStart, labelTaskEnd, taskPropertiesController); } private class WorkableDaysAndDatesBinder { private Intbox taskWorkableDays; private Label labelTaskStart; private Label labelTaskEnd; private final TaskPropertiesController taskPropertiesController; WorkableDaysAndDatesBinder(final Intbox taskWorkableDays, final Label labelTaskStart, final Label labelTaskEnd, final TaskPropertiesController taskPropertiesController) { this.taskWorkableDays = taskWorkableDays; this.labelTaskStart = labelTaskStart; this.labelTaskEnd = labelTaskEnd; this.taskPropertiesController = taskPropertiesController; initializeDateAndDurationFieldsFromTaskOriginalValues(); final LocalDate firstPossibleDay = getTask().getFirstDayNotConsolidated().nextDayAtStart().asExclusiveEnd(); Util.ensureUniqueListeners(taskWorkableDays, Events.ON_CHANGE, new EventListener() { @Override public void onEvent(Event event) { Task task = getTask(); Integer workableDays = taskWorkableDays.getValue(); if ( allocationRowsHandler.isForwardsAllocation() ) { IntraDayDate newEnd = ensureItIsAfterConsolidation(task.calculateEndGivenWorkableDays(workableDays)); updateWorkableDaysIfNecessary(workableDays, getTask().getIntraDayStartDate(), newEnd); taskPropertiesController.updateTaskEndDate(newEnd.getDate()); showValueOfDateOn(labelTaskEnd, newEnd.getDate()); } else { IntraDayDate newStart = ensureItIsAfterConsolidation(task.calculateStartGivenWorkableDays(workableDays)); updateWorkableDaysIfNecessary(workableDays, newStart, task.getIntraDayEndDate()); taskPropertiesController.updateTaskStartDate(newStart.getDate()); showValueOfDateOn(labelTaskStart, newStart.getDate()); } } private void updateWorkableDaysIfNecessary(int specifiedWorkableDays, IntraDayDate allocationStart, IntraDayDate allocationEnd) { Integer effectiveWorkableDays = getTask().getWorkableDaysFrom( allocationStart.getDate(), allocationEnd.asExclusiveEnd()); if ( effectiveWorkableDays < specifiedWorkableDays ) { Clients.response(new AuWrongValue( taskWorkableDays, _("The original workable days value {0} cannot be modified as it has consolidations", specifiedWorkableDays)) ); taskWorkableDays.setValue(effectiveWorkableDays); } } @SuppressWarnings("unchecked") private IntraDayDate ensureItIsAfterConsolidation(IntraDayDate newDate) { if ( getTask().hasConsolidations() ) { return Collections.max(Arrays.asList(newDate, IntraDayDate.startOfDay(firstPossibleDay))); } return newDate; } }, onChangeEnableApply); applyDisabledRules(); } void applyDisabledRules() { this.taskWorkableDays.setDisabled(allocationRowsHandler.getCalculatedValue() == CalculatedValue.END_DATE || isAnyManual() || isTaskUpdatedFromTimesheets()); } private void initializeDateAndDurationFieldsFromTaskOriginalValues() { Task task = getTask(); showValueOfDateOn(labelTaskStart, task.getStartAsLocalDate()); showValueOfDateOn(labelTaskEnd, task.getEndAsLocalDate()); taskWorkableDays.setConstraint(positiveValueRequired()); taskWorkableDays.setValue(task.getWorkableDays()); } Integer getValue() { return taskWorkableDays.getValue(); } private Integer lastSpecifiedWorkableDays = null; void switchFromTo(CalculatedValue previousCalculatedValue, CalculatedValue newCalculatedValue) { if ( newCalculatedValue == CalculatedValue.END_DATE ) { clearDateAndDurationFields(); } else if ( previousCalculatedValue == CalculatedValue.END_DATE && taskWorkableDays.getValue() == null ) { initializeDateAndDurationFieldsFromLastValues(); } } private void clearDateAndDurationFields() { (allocationRowsHandler.isForwardsAllocation() ? labelTaskEnd : labelTaskStart).setValue(""); taskWorkableDays.setConstraint((Constraint) null); lastSpecifiedWorkableDays = taskWorkableDays.getValue(); taskWorkableDays.setValue(null); } private void initializeDateAndDurationFieldsFromLastValues() { if ( lastSpecifiedWorkableDays == null ) { initializeDateAndDurationFieldsFromTaskOriginalValues(); } else { Task task = getTask(); taskWorkableDays.setConstraint(positiveValueRequired()); taskWorkableDays.setValue(lastSpecifiedWorkableDays); showValueOfDateOn(labelTaskEnd, task.calculateEndGivenWorkableDays(lastSpecifiedWorkableDays).getDate()); lastSpecifiedWorkableDays = null; } } void afterApplicationReloadValues() { if ( getCalculatedValue() != CalculatedValue.END_DATE || aggregate.isEmpty() ) { return; } LocalDate start = aggregate.getStart().getDate(); LocalDate end = aggregate.getEnd().asExclusiveEnd(); taskPropertiesController.updateTaskStartDate(start); taskPropertiesController.updateTaskEndDate(end); showValueOfDateOn(labelTaskStart, start); showValueOfDateOn(labelTaskEnd, end); taskWorkableDays.setValue(getTask().getWorkableDaysFrom(start, end)); } private void showValueOfDateOn(final Label label, LocalDate date) { DateTimeFormatter formatter = DateTimeFormat.forStyle("S-").withLocale(Locales.getCurrent()); label.setValue(formatter.print(date)); } } private static SimpleConstraint positiveValueRequired() { return new SimpleConstraint(SimpleConstraint.NO_EMPTY | SimpleConstraint.NO_NEGATIVE); } IntraDayDate getAllocationEnd() { return getTask().calculateEndGivenWorkableDays(workableDaysAndDatesBinder.getValue()); } IntraDayDate getAllocationStart() { return getTask().calculateStartGivenWorkableDays(workableDaysAndDatesBinder.getValue()); } private Task getTask() { return allocationRowsHandler.getTask(); } void setAllResourcesPerDay(Decimalbox allResourcesPerDay) { this.allResourcesPerDay = allResourcesPerDay; this.allResourcesPerDay.setConstraint(positiveValueRequired()); allResourcesPerDayVisibilityRule(); onChangeEnableApply(allResourcesPerDay); } private void allResourcesPerDayVisibilityRule() { CalculatedValue c = allocationRowsHandler.getCalculatedValue(); this.allResourcesPerDay.setDisabled(rows.isEmpty() || c == CalculatedValue.RESOURCES_PER_DAY || !recommendedAllocation || isAnyManual() || isTaskUpdatedFromTimesheets()); this.allResourcesPerDay.setConstraint(constraintForAllResourcesPerDay()); } private Constraint constraintForAllResourcesPerDay() { if ( allResourcesPerDay.isDisabled() ) { return null; } return AllocationRow.CONSTRAINT_FOR_RESOURCES_PER_DAY; } List<AllocationRow> getCurrentRows() { List<AllocationRow> result = addListeners(allocationRowsHandler.getCurrentRows()); rows = result; setupRows(); return result; } private void setupRows() { applyDisabledRulesOnRows(); bindTotalHoursToHoursInputs(); allHoursInputComponentDisabilityRule(); bindAllResourcesPerDayToRows(); allResourcesPerDayVisibilityRule(); loadAggregatedCalculations(); } private void bindAllResourcesPerDayToRows() { sumResourcesPerDayFromRowsAndAssignToAllResourcesPerDay(); for (AllocationRow each : rows) { each.addListenerForResourcesPerDayInputChange(resourcesPerDayRowInputChange); } } private List<AllocationRow> addListeners(List<AllocationRow> list) { for (AllocationRow each : list) { each.addListenerForInputChange(onChangeEnableApply); } return list; } private void doApply() { AllocationResult allocationResult = resourceAllocationModel.onAllocationContext(new IResourceAllocationContext<AllocationResult>() { @Override public AllocationResult doInsideTransaction() { return allocationRowsHandler.doAllocation().getValue(); } }); allocationProduced(allocationResult); TaskPropertiesController.allocationResult = allocationResult; } /** * @return <code>true</code> if and only if operation completed and must * exit the edition form */ public boolean accept() { if ( isTaskUpdatedFromTimesheets() ) { return true; } Flagged<AllocationResult, Warnings> result = resourceAllocationModel.accept(); // result can be null when editing milestones if ( result != null && result.isFlagged() ) { allocationProduced(result.getValue()); } return result == null || !result.isFlagged(); } private void allocationProduced(AllocationResult allocationResult) { lastAllocation = allocationResult; aggregate = lastAllocation.getAggregate(); loadResourcesPerDay(); loadEffortValues(); loadDerivedAllocations(); loadSclassRowSatisfied(); loadAssignmentFunctionNames(); workableDaysAndDatesBinder.afterApplicationReloadValues(); Util.reloadBindings(allocationsGrid); } @SuppressWarnings("unchecked") private void loadSclassRowSatisfied() { try { List<org.zkoss.zul.Row> rows = allocationsGrid.getRows().getChildren(); for (org.zkoss.zul.Row row : rows) { if ( row.getValue() instanceof AllocationRow ) { if ( !((AllocationRow) row.getValue()).isSatisfied() ) { row.setSclass("allocation-not-satisfied"); } else { row.setSclass("allocation-satisfied"); } } } } catch (ClassCastException e) { throw new RuntimeException(); } } private void loadAssignmentFunctionNames() { for (AllocationRow each : rows) { each.loadAssignmentFunctionName(); } } private void loadEffortValues() { for (AllocationRow each : rows) { each.loadEffort(); } } private void loadResourcesPerDay() { for (AllocationRow each : rows) { each.loadResourcesPerDay(); } } private void loadDerivedAllocations() { for (AllocationRow each : rows) { each.reloadDerivedAllocationsGrid(); } } void setApplyButton(Button applyButton) { this.applyButton = applyButton; this.applyButton.setDisabled(true); applyButtonListener = new EventListener() { @Override public void onEvent(Event event) { doApply(); FormBinder.this.applyButton.setDisabled(true); } }; Util.ensureUniqueListener(this.applyButton, Events.ON_CLICK, applyButtonListener); } EffortDuration getAssignedEffort() { EffortDuration result = effortInput.getEffortDurationValue(); if ( result == null ) { throw new RuntimeException("assignedHoursComponent returns null"); } return result; } Integer getWorkableDays() { return workableDaysAndDatesBinder.getValue(); } void setDeleteButtonFor(Button deleteButton) { deleteButton.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) { applyButton.setDisabled(false); } }); } void newAllocationAdded() { applyButton.setDisabled(false); } void markAssignedHoursMustBePositive() { throw new WrongValueException(effortInput, _("it must be greater than zero")); } void markRepeatedResources(List<Resource> resources) { messagesForUser.showMessage( Level.ERROR, _("{0} already assigned to resource allocation list", StringUtils.join(getResourcesDescriptions(resources), ", "))); } private List<String> getResourcesDescriptions(List<Resource> resources) { List<String> resourcesDescriptions = new ArrayList<>(); for (Resource each : resources) { resourcesDescriptions.add(each.getShortDescription()); } return resourcesDescriptions; } void markNoResourcesMatchedByCriterions(ResourceEnum resourceType, Collection<? extends Criterion> criterions) { messagesForUser.showMessage(Level.ERROR, _("there are no resources for required criteria: {0}. So the generic allocation can't be added", Criterion.getCaptionFor(resourceType, criterions))); } void markThereisAlreadyAssignmentWith(ResourceEnum resourceType, Collection<? extends Criterion> criterions) { messagesForUser.showMessage(Level.ERROR, _("already exists an allocation for criteria {0}", Criterion.getCaptionFor(resourceType, criterions))); } void markNoEmptyResourcesPerDay(List<AllocationRow> rows) { Validate.isTrue(!rows.isEmpty()); final String message = _("resources per day cannot be empty or less than zero"); if ( !recommendedAllocation ) { AllocationRow first = rows.get(0); throw new WrongValueException(first.getIntendedResourcesPerDayInput(), message); } else { throw new WrongValueException(allResourcesPerDay, message); } } void setAllocationsGrid(Grid allocationsGrid) { this.allocationsGrid = allocationsGrid; } public void setMessagesForUser(IMessagesForUser messages) { this.messagesForUser = messages; } public void detach() { if ( this.applyButton != null ) { this.applyButton.removeEventListener(Events.ON_CLICK, applyButtonListener); } for (InputElement inputElement : inputsAssociatedWithOnChangeEnableApply) { inputElement.removeEventListener(Events.ON_CHANGE, onChangeEnableApply); } } void setRecommendedAllocation(Button recommendedAllocation) { this.btnRecommendedAllocation = recommendedAllocation; this.btnRecommendedAllocation.setDisabled(isAnyManual() || isTaskUpdatedFromTimesheets()); Util.ensureUniqueListener(recommendedAllocation, Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) { activatingRecommendedAllocation(); } }); } public EventListener getRecommendedAllocationListener() { return new EventListener() { @Override public void onEvent(Event event) { activatingRecommendedAllocation(); } }; } private void activatingRecommendedAllocation() { allocationRowsHandler.removeAll(); ProportionalDistributor distributor = resourceAllocationModel.addDefaultAllocations(); boolean recommendAllocationSuccessful = distributor != null; if ( recommendAllocationSuccessful ) { hoursDistributorForRecommendedAllocation = distributor; resourcesPerDayDistributorForRecommendedAllocation = ResourcesPerDay.distributor(hoursDistributorForRecommendedAllocation); this.recommendedAllocation = true; disableIfNeededWorkerSearch(); applyDisabledRules(); effortInput.addEventListener(Events.ON_CHANGE, allHoursInputChange); allResourcesPerDay.addEventListener(Events.ON_CHANGE, allResourcesPerDayChange); resetStateForResourcesPerDayInputsWhenDoingRecommendedAllocation(); } Util.reloadBindings(allocationsGrid); } private void resetStateForResourcesPerDayInputsWhenDoingRecommendedAllocation() { if ( allResourcesPerDay.isDisabled() ) { allResourcesPerDay.setValue((BigDecimal) null); AllocationRow.unknownResourcesPerDay(rows); } else { allResourcesPerDay.setValue(BigDecimal.ONE); distributeResourcesPerDayToRows(); allResourcesPerDay.focus(); } } private void distributeHoursFromTotalToRows() { EffortDuration value = effortInput.getEffortDurationValue(); value = value != null ? value : zero(); int[] seconds = hoursDistributorForRecommendedAllocation.distribute(value.getSeconds()); assignEfforts(rows, asEfforts(seconds)); } private EffortDuration[] asEfforts(int[] seconds) { EffortDuration[] result = new EffortDuration[seconds.length]; for (int i = 0; i < result.length; i++) { result[i] = EffortDuration.seconds(seconds[i]); } return result; } private void distributeResourcesPerDayToRows() { BigDecimal total = allResourcesPerDay.getValue(); total = total != null ? total : BigDecimal.ZERO; ResourcesPerDay[] forRows = resourcesPerDayDistributorForRecommendedAllocation .distribute(ResourcesPerDay.amount(total)); AllocationRow.assignResourcesPerDay(rows, forRows); } void rowRemoved() { deactivatingRecommendedAllocation(); } private void deactivatingRecommendedAllocation() { this.recommendedAllocation = false; effortInput.removeEventListener(Events.ON_CHANGE, allHoursInputChange); allResourcesPerDay.removeEventListener(Events.ON_CHANGE, allResourcesPerDayChange); applyDisabledRules(); disableIfNeededWorkerSearch(); } private void disableIfNeededWorkerSearch() { workerSearchTab.setDisabled(this.recommendedAllocation); newAllocationSelectorCombo.setDisabled(this.recommendedAllocation); advancedSearchButton.setDisabled(this.recommendedAllocation); } void setWorkerSearchTab(Tab workerSearchTab) { this.workerSearchTab = workerSearchTab; this.workerSearchTab.setDisabled(recommendedAllocation); } void setAdvancedSearchButton(Button advancedSearchButton) { this.advancedSearchButton = advancedSearchButton; this.advancedSearchButton.setDisabled(recommendedAllocation); } void setNewAllocationSelectorCombo(NewAllocationSelectorCombo newAllocationSelectorCombo) { this.newAllocationSelectorCombo = newAllocationSelectorCombo; this.newAllocationSelectorCombo.setDisabled(recommendedAllocation); } private void sumResourcesPerDayFromRowsAndAssignToAllResourcesPerDay() { if ( allResourcesPerDay.isDisabled() ) { allResourcesPerDay.setValue(sumResourcesPerDayFromInputs()); } } private BigDecimal sumResourcesPerDayFromInputs() { BigDecimal sum = BigDecimal.ZERO; for (AllocationRow each : rows) { if ( each.getIntendedResourcesPerDayInput().isValid() ) { sum = sum.add(each.getResourcesPerDayFromInput().getAmount()); } } return sum; } private BigDecimal sumAllTotalResourcesPerDay() { BigDecimal sum = BigDecimal.ZERO; for (AllocationRow each : rows) { sum = sum.add(each.getTotalResourcesPerDay().getAmount()); } return sum; } private BigDecimal sumAllConsolidatedResourcesPerDay() { BigDecimal sum = BigDecimal.ZERO; for (AllocationRow each : rows) { sum = sum.add(each.getConsolidatedResourcesPerDay().getAmount()); } return sum; } void setAllOriginalEffort(Label allOriginalEffort) { this.allOriginalEffort = allOriginalEffort; } public Label getAllOriginalEffort() { return allOriginalEffort; } void setAllTotalEffort(Label allTotalHours) { this.allTotalEffort = allTotalHours; } public Label getAllTotalEffort() { return allTotalEffort; } void setAllConsolidatedEffort(Label allConsolidatedEffort) { this.allConsolidatedEffort = allConsolidatedEffort; } public Label getAllConsolidatedEffort() { return allConsolidatedEffort; } void setAllTotalResourcesPerDay(Label allTotalResourcesPerDay) { this.allTotalResourcesPerDay = allTotalResourcesPerDay; } public Label getAllTotalResourcesPerDay() { return allTotalResourcesPerDay; } void setAllConsolidatedResourcesPerDay(Label allConsolidatedResourcesPerDay) { this.allConsolidatedResourcesPerDay = allConsolidatedResourcesPerDay; } public Label getAllConsolidatedResourcesPerDay() { return allConsolidatedResourcesPerDay; } private void loadAggregatedCalculations() { // Calculate aggregated values if ( behaviour.allowMultipleSelection() ) { allOriginalEffort.setValue(sumAllOriginalEffort(this.rows).toFormattedString()); allTotalEffort.setValue(sumAllTotalEffort(this.rows).toFormattedString()); allConsolidatedEffort.setValue(AllocationRow.sumAllConsolidatedEffort(this.rows).toFormattedString()); allTotalResourcesPerDay.setValue(sumAllTotalResourcesPerDay().toString()); allConsolidatedResourcesPerDay.setValue(sumAllConsolidatedResourcesPerDay().toString()); } } public boolean allowMultipleSelection() { return behaviour.allowMultipleSelection(); } public void cannotAllocateMoreThanOneResource(List<Resource> resources) { messagesForUser.showMessage(Level.ERROR, _("{0} could not be allocated. " + "Cannot allocate more than one resource", Resource.getCaptionFor(resources))); } public void setBehaviour(ResourceAllocationBehaviour behaviour) { this.behaviour = behaviour; } boolean isAnyNotFlat() { for (AllocationRow allocationRow : allocationRowsHandler.getCurrentRows()) { if ( allocationRow.isAssignmentFunctionNotFlat() ) { return true; } } return false; } boolean isAnyManual() { for (AllocationRow allocationRow : allocationRowsHandler.getCurrentRows()) { if ( allocationRow.isAssignmentFunctionManual() ) { return true; } } return false; } boolean isTaskUpdatedFromTimesheets() { return allocationRowsHandler.isTaskUpdatedFromTimesheets(); } }