diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/Flagged.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/Flagged.java new file mode 100644 index 000000000..91c5c2d5a --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/Flagged.java @@ -0,0 +1,92 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 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 . + */ +package org.navalplanner.business.common; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Wraps a value with several arbitrary flags. + * + * @author Oscar Gonzalez Fernandez + * + * @param + * the value that can have flags associated with it + * @param + * the type of the flags + */ +public class Flagged { + + public static Flagged justValue(T value) { + return new Flagged(value, new HashSet()); + } + + public static Flagged withFlags(T value, F... flags) { + return new Flagged(value, new HashSet(Arrays.asList(flags))); + } + + private final T value; + + private final Set flags; + + private Flagged(T value, Set flags) { + this.value = value; + this.flags = Collections.unmodifiableSet(flags); + } + + public Flagged withFlag(F flag) { + Set newFlags = new HashSet(flags); + newFlags.add(flag); + return new Flagged(value, newFlags); + } + + public Flagged withoutFlag(F flag) { + Set newFlags = new HashSet(flags); + newFlags.remove(flag); + return new Flagged(value, newFlags); + } + + public T getValue() { + return value; + } + + public boolean isFlagged() { + return !this.flags.isEmpty(); + } + + public Set getFlags() { + return this.flags; + } + + public boolean isFlaggedWith(F flag){ + return this.flags.contains(flag); + } + + public boolean isFlaggedWithSomeOf(F... flags) { + for (F each : flags) { + if (this.isFlaggedWith(each)) { + return true; + } + } + return false; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocationModification.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocationModification.java index f79dbaec3..20c7aa2ff 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocationModification.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocationModification.java @@ -34,6 +34,16 @@ import org.navalplanner.business.resources.entities.Resource; */ public abstract class AllocationModification { + public static boolean allFullfiled( + Collection modificationsDone) { + for (AllocationModification each : modificationsDone) { + if (!each.satisfiesModificationRequested()) { + return false; + } + } + return true; + } + public static List> getBeingModified( Collection allocationModifications) { List> result = new ArrayList>(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRowsHandler.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRowsHandler.java index bc99c0ce2..92d857dc2 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRowsHandler.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRowsHandler.java @@ -29,6 +29,7 @@ import java.util.Set; import org.joda.time.LocalDate; import org.navalplanner.business.calendars.entities.ThereAreHoursOnWorkHoursCalculator.CapacityResult; +import org.navalplanner.business.common.Flagged; import org.navalplanner.business.orders.entities.HoursGroup; import org.navalplanner.business.planner.entities.CalculatedValue; import org.navalplanner.business.planner.entities.DerivedAllocationGenerator.IWorkerFinder; @@ -229,7 +230,11 @@ public class AllocationRowsHandler { return result; } - public AllocationResult doAllocation() { + public enum Warnings { + SOME_GOALS_NOT_FULFILLED; + } + + public Flagged doAllocation() { checkInvalidValues(); if (!currentRows.isEmpty()) { List modificationsDone; @@ -238,10 +243,20 @@ public class AllocationRowsHandler { AllocationRow.loadDataFromLast(currentRows, modificationsDone); createDerived(); + AllocationResult result = createResult(); + if (AllocationModification.allFullfiled(modificationsDone)) { + return Flagged.justValue(result); + } else { + return Flagged.withFlags(result, + Warnings.SOME_GOALS_NOT_FULFILLED); + } } - AllocationResult result = AllocationResult.create(task, + return Flagged.justValue(createResult()); + } + + private AllocationResult createResult() { + return AllocationResult.create(task, calculatedValue, currentRows, getWorkableDaysIfApplyable()); - return result; } private List doSuitableAllocation() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java index 4c7281596..ad9f44e1a 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java @@ -34,6 +34,7 @@ import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import org.navalplanner.business.common.Flagged; import org.navalplanner.business.common.ProportionalDistributor; import org.navalplanner.business.planner.entities.AggregateOfResourceAllocations; import org.navalplanner.business.planner.entities.CalculatedValue; @@ -48,6 +49,7 @@ import org.navalplanner.web.common.Level; import org.navalplanner.web.common.Util; import org.navalplanner.web.common.components.NewAllocationSelectorCombo; import org.navalplanner.web.common.components.ResourceAllocationBehaviour; +import org.navalplanner.web.planner.allocation.AllocationRowsHandler.Warnings; import org.navalplanner.web.planner.allocation.IResourceAllocationModel.IResourceAllocationContext; import org.navalplanner.web.planner.taskedition.TaskPropertiesController; import org.zkoss.util.Locales; @@ -486,14 +488,35 @@ public class FormBinder { } public void doApply() { - lastAllocation = resourceAllocationModel + AllocationResult allocationResult = resourceAllocationModel .onAllocationContext(new IResourceAllocationContext() { @Override public AllocationResult doInsideTransaction() { - return allocationRowsHandler.doAllocation(); + return allocationRowsHandler.doAllocation().getValue(); } }); + allocationProduced(allocationResult); + } + + /** + * + * @return true if and only if operation completed and must + * exit the edition form + */ + public boolean accept() { + Flagged 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(); allResourcesPerDayVisibilityRule(); sumResourcesPerDayFromRowsAndAssignToAllResourcesPerDay(); @@ -861,5 +884,4 @@ public class FormBinder { this.behaviour = behaviour; } - } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java index ef90b7405..8ad6e27f5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java @@ -24,10 +24,12 @@ package org.navalplanner.web.planner.allocation; import java.util.Date; import java.util.List; +import org.navalplanner.business.common.Flagged; import org.navalplanner.business.common.ProportionalDistributor; import org.navalplanner.business.orders.entities.AggregatedHoursGroup; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.web.planner.allocation.AllocationRowsHandler.Warnings; import org.navalplanner.web.planner.order.PlanningState; import org.zkoss.ganttz.extensions.IContextWithPlannerTask; @@ -50,8 +52,10 @@ public interface IResourceAllocationModel extends INewAllocationsAdder { /** * Save task + * + * @return */ - void accept(); + Flagged accept(); /** * Starts the use case diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java index 48c644446..85b2af27e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java @@ -597,9 +597,17 @@ public class ResourceAllocationController extends GenericForwardComposer { allocationsGrid.setModel(new SimpleListModel(Collections.emptyList())); } - public void accept() { - resourceAllocationModel.accept(); - clear(); + /** + * + * @return true if it must exist false if exit + * must be prevented + */ + public boolean accept() { + boolean mustExit = formBinder.accept(); + if (mustExit) { + clear(); + } + return mustExit; } private class ResourceAllocationRenderer implements RowRenderer { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java index debaa778d..18cb1973e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Set; import org.hibernate.Hibernate; +import org.navalplanner.business.common.Flagged; import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.common.ProportionalDistributor; @@ -39,11 +40,11 @@ import org.navalplanner.business.planner.daos.ITaskElementDAO; import org.navalplanner.business.planner.daos.ITaskSourceDAO; import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.DerivedAllocation; +import org.navalplanner.business.planner.entities.DerivedAllocationGenerator.IWorkerFinder; import org.navalplanner.business.planner.entities.GenericResourceAllocation; import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; -import org.navalplanner.business.planner.entities.DerivedAllocationGenerator.IWorkerFinder; import org.navalplanner.business.resources.daos.ICriterionDAO; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.daos.IResourcesSearcher; @@ -55,6 +56,7 @@ import org.navalplanner.business.resources.entities.MachineWorkersConfigurationU import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.entities.ResourceEnum; import org.navalplanner.business.resources.entities.Worker; +import org.navalplanner.web.planner.allocation.AllocationRowsHandler.Warnings; import org.navalplanner.web.planner.order.PlanningState; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -169,42 +171,52 @@ public class ResourceAllocationModel implements IResourceAllocationModel { } @Override - public void accept() { + public Flagged accept() { if (context != null) { - applyAllocationWithDateChangesNotification(new IOnTransaction() { + return applyDateChangesNotificationIfNoFlags(new IOnTransaction>() { @Override - public Void execute() { + public Flagged execute() { stepsBeforeDoingAllocation(); - allocationRowsHandler.doAllocation().applyTo( - planningState.getCurrentScenario(), task); - return null; + Flagged allocationResult = allocationRowsHandler + .doAllocation(); + if (!allocationResult.isFlagged()) { + allocationResult.getValue().applyTo( + planningState.getCurrentScenario(), task); + } + return allocationResult; } }); } + return null; } @Override public void accept(final AllocationResult modifiedAllocationResult) { if (context != null) { - applyAllocationWithDateChangesNotification(new IOnTransaction() { + applyDateChangesNotificationIfNoFlags(new IOnTransaction>() { @Override - public Void execute() { + public Flagged execute() { stepsBeforeDoingAllocation(); modifiedAllocationResult.applyTo(planningState .getCurrentScenario(), task); - return null; + + return Flagged.justValue(null); } }); } } - private void applyAllocationWithDateChangesNotification( - IOnTransaction allocationDoer) { + private > T applyDateChangesNotificationIfNoFlags( + IOnTransaction allocationDoer) { org.zkoss.ganttz.data.Task ganttTask = context.getTask(); GanttDate previousStartDate = ganttTask.getBeginDate(); GanttDate previousEnd = ganttTask.getEndDate(); - transactionService.runOnReadOnlyTransaction(allocationDoer); - ganttTask.fireChangesForPreviousValues(previousStartDate, previousEnd); + T result = transactionService.runOnReadOnlyTransaction(allocationDoer); + if (!result.isFlagged()) { + ganttTask.fireChangesForPreviousValues(previousStartDate, + previousEnd); + } + return result; } private void stepsBeforeDoingAllocation() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java index 73c7b62c5..8caf496cf 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java @@ -282,7 +282,10 @@ public class EditTaskController extends GenericForwardComposer { ResourceAllocationTypeEnum currentState = taskPropertiesController.getCurrentState(); if (ResourceAllocationTypeEnum.NON_LIMITING_RESOURCES.equals(currentState)) { editTaskTabbox.setSelectedPanelApi(resourceAllocationTabpanel); - resourceAllocationController.accept(); + boolean mustNotExit = !resourceAllocationController.accept(); + if (mustNotExit) { + return; + } } else if (ResourceAllocationTypeEnum.SUBCONTRACT.equals(currentState)) { editTaskTabbox.setSelectedPanelApi(subcontractTabpanel); subcontractController.accept();