diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java b/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java index 44524a81c..03ca5a742 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java @@ -256,7 +256,8 @@ public class TaskComponent extends Div implements AfterCompose { protected String calculateClass() { String classText; - if (getSclass().equals("null")) { + + if (getSclass() == null || getSclass().equals("null")) { classText = "box"; } else { classText = getSclass(); @@ -264,6 +265,9 @@ public class TaskComponent extends Div implements AfterCompose { if (task.isInCriticalPath()) { classText += " critical"; } + if (task.isSubcontracted()) { + classText += " subcontracted-task"; + } classText += " " + getTask().getAssignedStatus(); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java index 484456057..ac535ebbb 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java @@ -36,6 +36,7 @@ import org.hibernate.validator.Valid; import org.joda.time.DateTime; import org.joda.time.Days; import org.joda.time.LocalDate; +import org.navalplanner.business.common.Registry; import org.navalplanner.business.orders.entities.AggregatedHoursGroup; import org.navalplanner.business.orders.entities.HoursGroup; import org.navalplanner.business.orders.entities.OrderElement; @@ -141,6 +142,12 @@ public class Task extends TaskElement { return Collections.unmodifiableSet(resourceAllocations); } + public boolean isLimiting() { + // FIXME: Task is limiting if its resourceAllocation is associated with + // a LimitingResourceQueueElement + return false; + } + public void addResourceAllocation(ResourceAllocation resourceAllocation) { if (!resourceAllocation.getTask().equals(this)) { throw new IllegalArgumentException( @@ -500,4 +507,14 @@ public class Task extends TaskElement { return false; } + public void removeSubcontractCommunicationDate() { + if (subcontractedTaskData != null) { + subcontractedTaskData.setSubcontractCommunicationDate(null); + } + } + + public boolean hasResourceAllocations() { + return !resourceAllocations.isEmpty(); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IMachineDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IMachineDAO.java index ff7d93125..7d561ecf6 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IMachineDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IMachineDAO.java @@ -42,7 +42,7 @@ public interface IMachineDAO extends IIntegrationEntityDAO { * search machine by name/Code * */ - List findByNameOrCode(String name); + List findByNameOrCode(String name, boolean limitingResource); /** * Finds a {@link Machine} with the Code param that should be unique. diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IResourceDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IResourceDAO.java index a78b29400..180cf9bb7 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IResourceDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IResourceDAO.java @@ -38,21 +38,88 @@ import org.navalplanner.business.resources.entities.Worker; */ public interface IResourceDAO extends IIntegrationEntityDAO { - public List getWorkers(); - - public List getRealWorkers(); - - public List getVirtualWorkers(); - - public List getMachines(); /** - * Returns all {@link Resource} which satisfy a set of {@link Criterion} + * Returns all {@link Resource} which are related with tasks + * + * @param tasks + * @return + */ + List findResourcesRelatedTo(List tasks); + + /** + * Returns a list of {@link Resource} satisfying all criterions + * + * @param criterions + * @return + */ + List findSatisfyingAllCriterions( + Collection criterions, + boolean limitingResource); + + /** + * Returns a list of {@link Resource} satisfying at least one criterion from criterions + * + * @param criterions + * @return */ List findSatisfyingCriterionsAtSomePoint(Collection criterions); - List findResourcesRelatedTo(List tasks); + /** + * Returns all {@link Machine} + * + * @return + */ + List getMachines(); + /** + * Returns all real resources ({@link Machine} and {@link Worker}) + * + * @return + */ + List getRealResources(); + + /** + * Returns all {@link Worker} which are not virtual + * + * @return + */ + List getRealWorkers(); + + /** + * Returns all {@link Resource} + * + * @return + */ List getResources(); - List getRealResources(); + /** + * Returns all {@link Worker} which are virtual + * + * @return + */ + List getVirtualWorkers(); + + /** + * Returns all {@link Worker} (including those which are virtual) + * + * @return + */ + List getWorkers(); + + /** + * + * Returns all {@link Resource} which are limiting + * + * @return + */ + List getAllLimitingResources(); + + /** + * + * Returns all {@link Resource} which are not limiting + * + * @return + */ + List getAllNonLimitingResources(); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IWorkerDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IWorkerDAO.java index 7e2c3aeab..d12d00ed9 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IWorkerDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/IWorkerDAO.java @@ -46,7 +46,7 @@ public interface IWorkerDAO extends IIntegrationEntityDAO { * search worker by name(firstname or surname)/NIF * */ - List findByNameSubpartOrNifCaseInsensitive(String name); + List findByNameSubpartOrNifCaseInsensitive(String name, boolean limitingResource); /** * Finds a {@link Worker} with the NIF param that should be unique. diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/MachineDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/MachineDAO.java index e8a48394d..dd19f3075 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/MachineDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/MachineDAO.java @@ -52,11 +52,14 @@ public class MachineDAO extends IntegrationEntityDAO @SuppressWarnings("unchecked") @Override - public List findByNameOrCode(String name) { - String containsName = "%" + name + "%"; + public List findByNameOrCode(String name, boolean limitingResource) { + final String containsName = "%" + name + "%"; return getSession().createCriteria(Machine.class).add( - Restrictions.or(Restrictions.ilike("name", containsName), - Restrictions.ilike("code", containsName))).list(); + Restrictions.and( + Restrictions.eq("limitingResource",limitingResource), + Restrictions.or( + Restrictions.ilike("name", containsName), + Restrictions.ilike("code", containsName)))).list(); } @Override diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ResourceDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ResourceDAO.java index 172e7d430..427f81623 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ResourceDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ResourceDAO.java @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.commons.lang.Validate; import org.hibernate.Query; +import org.hibernate.criterion.Restrictions; import org.navalplanner.business.common.daos.IntegrationEntityDAO; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.resources.entities.Criterion; @@ -104,19 +105,30 @@ public class ResourceDAO extends IntegrationEntityDAO implements return (List) query.list(); } + @Override + public List findSatisfyingAllCriterions( + Collection criteria, + boolean limitingResource) { + + return selectSatisfiyingAllCriterions(new ArrayList( + getResources()), criteria, limitingResource); + } + private List selectSatisfiyingAllCriterions( List resources, - Collection criterions) { + Collection criterions, + Boolean limitingResource) { + List result = new ArrayList(); for (Resource each : resources) { - if (each.satisfiesCriterions(criterions)) { + if (limitingResource.equals(each.isLimitingResource()) + && each.satisfiesCriterions(criterions)) { result.add(each); } } return result; } - @Override public List findResourcesRelatedTo(List taskElements) { if (taskElements.isEmpty()) { @@ -155,6 +167,20 @@ public class ResourceDAO extends IntegrationEntityDAO implements return list(Resource.class); } + @SuppressWarnings("unchecked") + @Override + public List getAllLimitingResources() { + return getSession().createCriteria(Resource.class).add( + Restrictions.eq("limitingResource", true)).list(); + } + + @SuppressWarnings("unchecked") + @Override + public List getAllNonLimitingResources() { + return getSession().createCriteria(Resource.class).add( + Restrictions.eq("limitingResource", false)).list(); + } + @Override public List getMachines() { return list(Machine.class); @@ -167,4 +193,5 @@ public class ResourceDAO extends IntegrationEntityDAO implements list.addAll(getMachines()); return list; } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/WorkerDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/WorkerDAO.java index fe536f197..5de16d2f2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/WorkerDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/WorkerDAO.java @@ -78,14 +78,17 @@ public class WorkerDAO extends IntegrationEntityDAO @SuppressWarnings("unchecked") @Override - public List findByNameSubpartOrNifCaseInsensitive(String name) { - String containsName = "%" + name + "%"; - return getSession().createCriteria(Worker.class) - .add( - Restrictions.or(Restrictions.or(Restrictions.ilike( - "firstName", containsName), Restrictions.ilike( - "surname", containsName)), Restrictions.like( - "nif", containsName))).list(); + public List findByNameSubpartOrNifCaseInsensitive(String name, boolean limitingResource) { + final String containsName = "%" + name + "%"; + return getSession().createCriteria(Worker.class).add( + Restrictions.and( + Restrictions.eq("limitingResource", limitingResource), + Restrictions.or( + Restrictions.or( + Restrictions.ilike("firstName", containsName), + Restrictions.ilike("surname", containsName)), + Restrictions.like("nif", containsName)))).list(); + } @SuppressWarnings("unchecked") diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/daos/ResourceDAOTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/daos/ResourceDAOTest.java index 3b5ed52cb..f7178141a 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/daos/ResourceDAOTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/daos/ResourceDAOTest.java @@ -21,18 +21,35 @@ package org.navalplanner.business.test.resources.daos; import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertThat; import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + import org.hibernate.SessionFactory; import org.junit.Test; import org.junit.runner.RunWith; import org.navalplanner.business.calendars.entities.ResourceCalendar; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.resources.daos.ICriterionDAO; +import org.navalplanner.business.resources.daos.ICriterionTypeDAO; import org.navalplanner.business.resources.daos.IResourceDAO; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionSatisfaction; +import org.navalplanner.business.resources.entities.CriterionType; +import org.navalplanner.business.resources.entities.Interval; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.entities.Worker; import org.springframework.beans.factory.annotation.Autowired; @@ -57,6 +74,12 @@ public class ResourceDAOTest { @Autowired private SessionFactory sessionFactory; + @Autowired + private ICriterionDAO criterionDAO; + + @Autowired + private ICriterionTypeDAO criterionTypeDAO; + @Test public void saveResourceWithCalendar() throws InstanceNotFoundException { Resource resource = givenValidWorker(); @@ -89,4 +112,70 @@ public class ResourceDAOTest { return worker; } + @Test + public void testResourceIsRelatedWithAllCriterions() { + Collection criterions = createCriterions(); + createAndSaveResourceSatisfyingAllCriterions(criterions); + List result = resourceDAO + .findSatisfyingAllCriterions(criterions, false); + assertNotNull(result); + assertEquals(1, result.size()); + } + + private Collection createCriterions() { + List result = new ArrayList(); + CriterionType type = createCriterionType("criterionTypeTest"); + result.add(createCriterion("criterion1", type)); + result.add(createCriterion("criterion2", type)); + return result; + } + + private CriterionType createCriterionType(String name) { + CriterionType result = CriterionType.create(name, ""); + criterionTypeDAO.save(result); + return result; + } + + private Criterion createCriterion(String name) { + return createCriterion(name, createCriterionType(UUID.randomUUID().toString())); + } + + private Criterion createCriterion(String name, CriterionType type) { + Criterion result = Criterion.create(name, type); + criterionDAO.save(result); + return result; + } + + private Worker createAndSaveResourceSatisfyingAllCriterions(final Collection criterions) { + Worker result = givenValidWorker(); + + SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); + try { + Interval interval = Interval.range(sdf.parse("01/01/1970"), null); + Set satisfactions = new HashSet(); + for (Criterion each: criterions) { + satisfactions.add(CriterionSatisfaction.create(each, result, interval)); + } + result.addSatisfactions(satisfactions); + resourceDAO.save(result); + } catch (ParseException e) { + + } + return result; + } + + @Test + public void testResourceIsNotRelatedWithAllCriterions() { + Collection criterions = createCriterions(); + createAndSaveResourceSatisfyingAllCriterions(criterions); + + // Modify criterions collection + criterions.add(createCriterion("criterion3")); + + List result = resourceDAO + .findSatisfyingAllCriterions(criterions, false); + assertNotNull(result); + assertTrue(result.size() != 1); + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/NewAllocationSelector.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/NewAllocationSelector.java index 5e181c52a..adc0239b3 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/NewAllocationSelector.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/NewAllocationSelector.java @@ -105,7 +105,6 @@ public class NewAllocationSelector extends HtmlMacroComponent { } public void addChoosen() { - getController().addTo(allocationsAdder); } @@ -117,4 +116,12 @@ public class NewAllocationSelector extends HtmlMacroComponent { } + public void setLimitingResourceFilter(boolean limitingResource) { + getController().setLimitingResourceFilter(limitingResource); + } + + public void allowSelectMultipleResources(boolean multiple) { + getController().allowSelectMultipleResources(multiple); + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ILimitingResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ILimitingResourceAllocationModel.java new file mode 100644 index 000000000..852cd5876 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ILimitingResourceAllocationModel.java @@ -0,0 +1,46 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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.web.planner.allocation; + +import java.util.List; + +import org.navalplanner.business.orders.entities.AggregatedHoursGroup; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.web.planner.allocation.LimitingResourceAllocationModel.LimitingResourceAllocationRow; + +/** + * Contract for {@link Task}. + * + * @author Diego Pino García + */ +public interface ILimitingResourceAllocationModel extends INewAllocationsAdder { + + List getHoursAggregatedByCriteria(); + + Integer getOrderHours(); + + List getResourceAllocations(); + + void init(Task task); + + void removeAllResourceAllocations(); + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java new file mode 100644 index 000000000..825d8ef99 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java @@ -0,0 +1,237 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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.web.planner.allocation; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.navalplanner.business.orders.entities.AggregatedHoursGroup; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.web.common.ConstraintChecker; +import org.navalplanner.web.common.IMessagesForUser; +import org.navalplanner.web.common.Util; +import org.navalplanner.web.common.components.NewAllocationSelector; +import org.navalplanner.web.planner.allocation.LimitingResourceAllocationModel.LimitingResourceAllocationRow; +import org.navalplanner.web.planner.allocation.ResourceAllocationController.HoursRendererColumn; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.zkoss.ganttz.timetracker.ICellForDetailItemRenderer; +import org.zkoss.ganttz.timetracker.OnColumnsRowRenderer; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Grid; +import org.zkoss.zul.Intbox; +import org.zkoss.zul.Label; +import org.zkoss.zul.ListModelList; +import org.zkoss.zul.Listbox; +import org.zkoss.zul.Listcell; +import org.zkoss.zul.Listitem; +import org.zkoss.zul.Row; +import org.zkoss.zul.RowRenderer; +import org.zkoss.zul.Tab; + +/** + * Controller for {@link ResourceAllocation} view. + * + * @author Diego Pino Garcia + */ +@org.springframework.stereotype.Component("limitingResourceAllocationController") +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class LimitingResourceAllocationController extends GenericForwardComposer { + + private static final Log LOG = LogFactory + .getLog(LimitingResourceAllocationController.class); + + @Autowired + private ILimitingResourceAllocationModel resourceAllocationModel; + + private Tab tabLimitingResourceAllocation; + + private Grid gridLimitingOrderElementHours; + + private Grid gridLimitingAllocations; + + private NewAllocationSelector limitingNewAllocationSelector; + + private GridLimitingAllocationRenderer gridLimitingAllocationRenderer = new GridLimitingAllocationRenderer(); + + @Override + public void doAfterCompose(Component comp) throws Exception { + super.doAfterCompose(comp); + limitingNewAllocationSelector.setLimitingResourceFilter(true); + limitingNewAllocationSelector.allowSelectMultipleResources(false); + } + + /** + * Shows Resource Allocation window + * @param task + * @param ganttTask + * @param planningState + */ + public void init(org.navalplanner.business.planner.entities.Task task, + IMessagesForUser messagesForUser) { + try { + resourceAllocationModel.init(task); + limitingNewAllocationSelector.setAllocationsAdder(resourceAllocationModel); + gridLimitingOrderElementHours.setModel(new ListModelList( + resourceAllocationModel.getHoursAggregatedByCriteria())); + gridLimitingOrderElementHours.setRowRenderer(createOrderElementHoursRenderer()); + } catch (Exception e) { + + } + } + + private static final ICellForDetailItemRenderer hoursCellRenderer = + new ICellForDetailItemRenderer() { + + @Override + public Component cellFor(HoursRendererColumn column, + AggregatedHoursGroup data) { + return column.cell(column, data); + } + }; + + private RowRenderer createOrderElementHoursRenderer() { + return OnColumnsRowRenderer.create(hoursCellRenderer, Arrays + .asList(HoursRendererColumn.values())); + } + + public Integer getOrderHours() { + return resourceAllocationModel.getOrderHours(); + } + + public List getResourceAllocations() { + return resourceAllocationModel.getResourceAllocations(); + } + + public void removeAllResourceAllocations() { + resourceAllocationModel.removeAllResourceAllocations(); + } + + public void onSelectWorkers(Event event) { + try { + addSelectedResources(); + } finally { + tabLimitingResourceAllocation.setSelected(true); + limitingNewAllocationSelector.clearAll(); + Util.reloadBindings(gridLimitingAllocations); + } + } + + private void addSelectedResources() { + resourceAllocationModel.removeAllResourceAllocations(); + limitingNewAllocationSelector.addChoosen(); + } + + public void onCloseSelectWorkers() { + clear(); + } + + public void clear() { + resourceAllocationModel.removeAllResourceAllocations(); + limitingNewAllocationSelector.clearAll(); + } + + public GridLimitingAllocationRenderer getGridLimitingAllocationRenderer() { + return gridLimitingAllocationRenderer; + } + + public class GridLimitingAllocationRenderer implements RowRenderer { + + @Override + public void render(Row row, Object data) throws Exception { + LimitingResourceAllocationRow resourceAllocation = (LimitingResourceAllocationRow) data; + + row.appendChild(label(resourceAllocation.getAllocationType())); + row.appendChild(label(resourceAllocation.getAllocation())); + row.appendChild(intboxHours(resourceAllocation)); + row.appendChild(listboxPriority(resourceAllocation)); + } + + private Label label(String value) { + return new Label(value); + } + + private Intbox intboxHours(final LimitingResourceAllocationRow resourceAllocation) { + return bindToHours(new Intbox(), resourceAllocation); + } + + private Intbox bindToHours(Intbox intbox, final LimitingResourceAllocationRow resourceAllocation) { + Util.bind(intbox, new Util.Getter() { + + @Override + public Integer get() { + return resourceAllocation.getHours(); + } + + }, new Util.Setter() { + + @Override + public void set(Integer value) { + resourceAllocation.setHours(value); + } + }); + return intbox; + } + + private Listbox listboxPriority(final LimitingResourceAllocationRow resourceAllocation) { + return bindToPriority(buildPriorityList(resourceAllocation.getPriority()), resourceAllocation); + } + + private Listbox buildPriorityList(int selectedValue) { + Listbox result = listbox(); + for (int i = 1; i <= 10; i++) { + Listitem item = new Listitem(); + Listcell cell = new Listcell(new Integer(i).toString()); + cell.setParent(item); + if (i == selectedValue) { + item.setSelected(true); + } + item.setParent(result); + } + return result; + } + + private Listbox listbox() { + Listbox result = new Listbox(); + result.setMold("select"); + return result; + } + + private Listbox bindToPriority(Listbox listbox, final LimitingResourceAllocationRow resourceAllocation) { + listbox.addEventListener("onSelect", new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + resourceAllocation.setPriorityStr((String) event.getData()); + } + }); + return listbox; + } + + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java new file mode 100644 index 000000000..fefa3d88c --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java @@ -0,0 +1,250 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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.web.planner.allocation; + +import static org.navalplanner.business.i18n.I18nHelper._; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.math.NumberUtils; +import org.navalplanner.business.orders.daos.IHoursGroupDAO; +import org.navalplanner.business.orders.entities.AggregatedHoursGroup; +import org.navalplanner.business.orders.entities.HoursGroup; +import org.navalplanner.business.orders.entities.TaskSource; +import org.navalplanner.business.planner.daos.ITaskSourceDAO; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.resources.daos.ICriterionDAO; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionType; +import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.web.common.components.NewAllocationSelector.AllocationType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * Provides logical operations for limiting resource assignations in @{Task} + * + * @author Diego Pino García + */ +@Service +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class LimitingResourceAllocationModel implements ILimitingResourceAllocationModel { + + @Autowired + private IHoursGroupDAO hoursGroupDAO; + + @Autowired + private ITaskSourceDAO taskSourceDAO; + + @Autowired + private ICriterionDAO criterionDAO; + + private Task task; + + private List resourceAllocations = new ArrayList(); + + @Override + public void init(Task task) { + this.task = task; + this.resourceAllocations = new ArrayList(); + } + + @Override + public Integer getOrderHours() { + if (task == null) { + return 0; + } + return AggregatedHoursGroup.sum(task.getAggregatedByCriterions()); + } + + public class LimitingResourceAllocationRow { + + private static final int DEFAULT_PRIORITY = 5; + + private AllocationType type = AllocationType.GENERIC; + + private int hours = 0; + + private int priority = DEFAULT_PRIORITY; + + public LimitingResourceAllocationRow() { + + } + + public LimitingResourceAllocationRow(AllocationType type, int hours) { + this(type, hours, DEFAULT_PRIORITY); + } + + public LimitingResourceAllocationRow(AllocationType type, int hours, int priority) { + this.type = type; + this.hours = hours; + this.priority = priority; + } + + public String getAllocationType() { + return type.toString(); + } + + public String getAllocation() { + if (AllocationType.GENERIC.equals(type)) { + return _("Criteria"); + } + if (AllocationType.SPECIFIC.equals(type)) { + return _("Resource"); + } + return ""; + } + + public int getHours() { + return hours; + } + + public void setHours(int hours) { + this.hours = hours; + } + + public int getPriority() { + return priority; + } + + public String getPriorityStr() { + return (new Integer(priority)).toString(); + } + + public void setPriorityStr(String priority) { + this.priority = toNumber(priority); + } + + private int toNumber(String str) { + if (NumberUtils.isNumber(str)) { + int result = NumberUtils.toInt(str); + return (result >= 1 && result <= 10) ? result : 1; + } + return 1; + } + + }; + + private void addSpecificResourceAllocation(Resource resource) { + LimitingResourceAllocationRow resourceAllocation = new LimitingResourceAllocationRow( + AllocationType.SPECIFIC, getSumHoursGroups()); + resourceAllocations.add(resourceAllocation); + } + + private int getSumHoursGroups() { + return task.getTaskSource().getTotalHours(); + } + + private void addGenericResourceAllocation(Resource resource) { + LimitingResourceAllocationRow resourceAllocation = new LimitingResourceAllocationRow( + AllocationType.GENERIC, getSumHoursGroups()); + resourceAllocations.add(resourceAllocation); + } + + @Override + public void addGeneric(Set criterions, + Collection resources) { + if (resources.size() >= 1) { + addGenericResourceAllocation(getFirstChild(resources)); + } + } + + @Override + public void addSpecific(Collection resources) { + if (resources.size() >= 1) { + addSpecificResourceAllocation(getFirstChild(resources)); + } + } + + public Resource getFirstChild(Collection collection) { + return collection.iterator().next(); + } + + @Override + public List getResourceAllocations() { + return Collections.unmodifiableList(resourceAllocations); + } + + @Override + @Transactional(readOnly = true) + public List getHoursAggregatedByCriteria() { + reattachTaskSource(); + List result = task.getTaskSource() + .getAggregatedByCriterions(); + ensuringAccesedPropertiesAreLoaded(result); + return result; + } + + private void ensuringAccesedPropertiesAreLoaded( + List result) { + for (AggregatedHoursGroup each : result) { + each.getCriterionsJoinedByComma(); + each.getHours(); + } + } + + /** + * Re-attach {@link TaskSource} + */ + private void reattachTaskSource() { + TaskSource taskSource = task.getTaskSource(); + taskSourceDAO.reattach(taskSource); + Set hoursGroups = taskSource.getHoursGroups(); + for (HoursGroup hoursGroup : hoursGroups) { + reattachHoursGroup(hoursGroup); + } + } + + private void reattachHoursGroup(HoursGroup hoursGroup) { + hoursGroupDAO.reattachUnmodifiedEntity(hoursGroup); + hoursGroup.getPercentage(); + reattachCriteria(hoursGroup.getValidCriterions()); + } + + private void reattachCriteria(Set criterions) { + for (Criterion criterion : criterions) { + reattachCriterion(criterion); + } + } + + private void reattachCriterion(Criterion criterion) { + criterionDAO.reattachUnmodifiedEntity(criterion); + criterion.getName(); + reattachCriterionType(criterion.getType()); + } + + private void reattachCriterionType(CriterionType criterionType) { + criterionType.getName(); + } + + @Override + public void removeAllResourceAllocations() { + resourceAllocations.clear(); + } + +} 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 de133824d..d4cc9a606 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 @@ -138,6 +138,7 @@ public class ResourceAllocationController extends GenericForwardComposer { super.doAfterCompose(comp); tabpanel = (Tabpanel) comp; allResourcesPerDay = new Decimalbox(); + newAllocationSelector.setLimitingResourceFilter(false); makeReadyInputsForCalculationTypes(); prepareCalculationTypesGrid(); } @@ -479,7 +480,7 @@ public class ResourceAllocationController extends GenericForwardComposer { resourceAllocationModel.cancel(); } - private void clear() { + public void clear() { newAllocationSelector.clearAll(); allocationsGrid.setModel(new SimpleListModel(Collections.emptyList())); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SubcontractController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SubcontractController.java index 8c3a1babc..e16707244 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SubcontractController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SubcontractController.java @@ -69,24 +69,7 @@ public class SubcontractController extends GenericForwardComposer { } public void accept() throws ValidationException { - int status = Messagebox.YES; - if (subcontractModel.hasResourceAllocations() - && (subcontractModel.getSubcontractedTaskData() != null)) { - try { - status = Messagebox - .show( - _("As you are subcontracting this task, all the resource allocations related with this task will be removed.") - + _("Are you sure?"), _("Confirm"), - Messagebox.YES | Messagebox.NO, - Messagebox.QUESTION); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - if (status == Messagebox.YES) { - subcontractModel.confirm(); - } + subcontractModel.confirm(); } public void cancel() { 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 606da356c..c66835aa6 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 @@ -35,12 +35,14 @@ import org.navalplanner.web.common.MessagesForUser; import org.navalplanner.web.common.Util; import org.navalplanner.web.planner.allocation.AllocationResult; import org.navalplanner.web.planner.allocation.FormBinder; +import org.navalplanner.web.planner.allocation.LimitingResourceAllocationController; import org.navalplanner.web.planner.allocation.ResourceAllocationController; import org.navalplanner.web.planner.allocation.AdvancedAllocationController.IAdvanceAllocationResultReceiver; import org.navalplanner.web.planner.allocation.AdvancedAllocationController.Restriction; import org.navalplanner.web.planner.allocation.AdvancedAllocationController.Restriction.IRestrictionSource; import org.navalplanner.web.planner.order.PlanningState; import org.navalplanner.web.planner.order.SubcontractController; +import org.navalplanner.web.planner.taskedition.TaskPropertiesController.ResourceAllocationTypeEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -67,17 +69,25 @@ public class EditTaskController extends GenericForwardComposer { @Autowired private ResourceAllocationController resourceAllocationController; + @Autowired + private LimitingResourceAllocationController limitingResourceAllocationController; + @Autowired private SubcontractController subcontractController; private Window window; private Tabbox editTaskTabbox; + private Tab resourceAllocationTab; + private Tab limitingResourceAllocationTab; private Tab subcontractTab; + private Tabpanel taskPropertiesTabpanel; private Tabpanel resourceAllocationTabpanel; + private Tabpanel limitingResourceAllocationTabpanel; private Tabpanel subcontractTabpanel; + private Component messagesContainer; private IMessagesForUser messagesForUser; @@ -95,6 +105,7 @@ public class EditTaskController extends GenericForwardComposer { taskPropertiesController.doAfterCompose(taskPropertiesTabpanel); resourceAllocationController.doAfterCompose(resourceAllocationTabpanel); subcontractController.doAfterCompose(subcontractTabpanel); + limitingResourceAllocationController.doAfterCompose(limitingResourceAllocationTabpanel); messagesForUser = new MessagesForUser(messagesContainer); } @@ -106,6 +117,10 @@ public class EditTaskController extends GenericForwardComposer { return resourceAllocationController; } + public LimitingResourceAllocationController getLimitingResourceAllocationController() { + return limitingResourceAllocationController; + } + public SubcontractController getSubcontractController() { return subcontractController; } @@ -116,24 +131,67 @@ public class EditTaskController extends GenericForwardComposer { this.context = context; this.planningState = planningState; - taskPropertiesController.init(context, taskElement); + taskPropertiesController.init(this, context, taskElement); if (taskElement instanceof Task) { resourceAllocationController.init(context, (Task) taskElement, planningState, messagesForUser); + limitingResourceAllocationController.init((Task) taskElement, messagesForUser); if (taskElement.isSubcontracted()) { subcontractController.init((Task) taskElement, context); } } try { - Util.reloadBindings(window); window.setTitle(_("Edit task: {0}", taskElement.getName())); window.setMode("modal"); + showSelectedTabPanel(); + Util.reloadBindings(window); } catch (InterruptedException e) { throw new RuntimeException(e); } } + private void showSelectedTabPanel() { + showTabPanel(taskPropertiesController + .getResourceAllocationType(taskElement)); + } + + public void showTabPanel( + ResourceAllocationTypeEnum resourceAllocationType) { + subcontractTab.setVisible(false); + resourceAllocationTab.setVisible(false); + limitingResourceAllocationTab.setVisible(false); + + if (ResourceAllocationTypeEnum.SUBCONTRACT + .equals(resourceAllocationType)) { + subcontractController.init(asTask(taskElement), context); + showSubcontractTab(); + } else if (ResourceAllocationTypeEnum.NON_LIMITING_RESOURCES + .equals(resourceAllocationType)) { + resourceAllocationController.init(context, asTask(taskElement), planningState, messagesForUser); + showNonLimitingResourcesTab(); + } else if (ResourceAllocationTypeEnum.LIMITING_RESOURCES + .equals(resourceAllocationType)) { + limitingResourceAllocationController.init(asTask(taskElement), messagesForUser); + showLimitingResourcesTab(); + } + + } + + private void showSubcontractTab() { + subcontractTab.setVisible(true); + } + + private void showNonLimitingResourcesTab() { + resourceAllocationController.clear(); + resourceAllocationTab.setVisible(true); + } + + private void showLimitingResourcesTab() { + limitingResourceAllocationController.clear(); + limitingResourceAllocationTab.setVisible(true); + } + public void showEditFormTaskProperties( IContextWithPlannerTask context, TaskElement taskElement, PlanningState planningState) { @@ -165,14 +223,22 @@ public class EditTaskController extends GenericForwardComposer { public void accept() { try { + if (taskPropertiesController.stateHasChanged()) { + ResourceAllocationTypeEnum oldState = taskPropertiesController.getOriginalState(); + removeAssociatedData(oldState); + } + editTaskTabbox.setSelectedPanelApi(taskPropertiesTabpanel); taskPropertiesController.accept(); - editTaskTabbox.setSelectedPanelApi(resourceAllocationTabpanel); - resourceAllocationController.accept(); - - editTaskTabbox.setSelectedPanelApi(subcontractTabpanel); - subcontractController.accept(); + ResourceAllocationTypeEnum currentState = taskPropertiesController.getCurrentState(); + if (ResourceAllocationTypeEnum.NON_LIMITING_RESOURCES.equals(currentState)) { + editTaskTabbox.setSelectedPanelApi(resourceAllocationTabpanel); + resourceAllocationController.accept(); + } else if (ResourceAllocationTypeEnum.SUBCONTRACT.equals(currentState)) { + editTaskTabbox.setSelectedPanelApi(subcontractTabpanel); + subcontractController.accept(); + } askForReloads(); @@ -185,6 +251,20 @@ public class EditTaskController extends GenericForwardComposer { } } + private void removeAssociatedData(ResourceAllocationTypeEnum state) { + Task task = asTask(taskElement); + + if (state.equals(ResourceAllocationTypeEnum.SUBCONTRACT)) { + task.removeSubcontractCommunicationDate(); + task.setSubcontractedTaskData(null); + subcontractController.removeSubcontractedTaskData(); + } + } + + public Task asTask(TaskElement taskElement) { + return (Task) taskElement; + } + private void askForReloads() { if (context != null) { context.getTask().reloadResourcesText(); @@ -203,58 +283,24 @@ public class EditTaskController extends GenericForwardComposer { window.setVisible(false); } - public void subcontract(boolean subcontract) { - if (taskElement instanceof Task) { - if (subcontract) { - resourceAllocationTab.setVisible(false); - subcontractTab.setVisible(true); - subcontractController.init((Task) taskElement, context); - } else { - subcontractTab.setVisible(false); - resourceAllocationTab.setVisible(true); - subcontractController.removeSubcontractedTaskData(); - } - } - } - public boolean isSubcontractedAndIsTask() { - if (taskElement == null) { - return false; - } - if (!isTask()) { - return false; - } - return taskElement.isSubcontracted(); + return isSubcontractedAndIsTask(taskElement); } private boolean isSubcontractedAndIsTask(TaskElement task) { - if (task == null) { - return false; - } - if (!(task instanceof Task)) { - return false; - } - return task.isSubcontracted(); + return (isTask(task) && task.isSubcontracted()); + } + + private boolean isTask(TaskElement taskElement) { + return (taskElement != null && taskElement instanceof Task); } public boolean isNotSubcontractedAndIsTask() { - if (taskElement == null) { - return false; - } - if (!isTask()) { - return false; - } - return !taskElement.isSubcontracted(); + return isNotSubcontractedAndIsTask(taskElement); } private boolean isNotSubcontractedAndIsTask(TaskElement task) { - if (task == null) { - return false; - } - if (!(task instanceof Task)) { - return false; - } - return !task.isSubcontracted(); + return (isTask(task) && !task.isSubcontracted()); } public void goToAdvancedAllocation() { @@ -329,7 +375,7 @@ public class EditTaskController extends GenericForwardComposer { } public boolean isTask() { - return (taskElement instanceof Task); + return isTask(taskElement); } public Date getStartConstraintDate() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java index 7b8648713..cce341ac8 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java @@ -21,7 +21,11 @@ package org.navalplanner.web.planner.taskedition; import static org.navalplanner.web.I18nHelper._; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; +import java.util.Iterator; +import java.util.List; import org.navalplanner.business.planner.entities.StartConstraintType; import org.navalplanner.business.planner.entities.Task; @@ -37,10 +41,14 @@ import org.zkoss.zk.ui.Component; 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.event.SelectEvent; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Comboitem; import org.zkoss.zul.Intbox; -import org.zkoss.zul.api.Checkbox; +import org.zkoss.zul.Listbox; +import org.zkoss.zul.Listcell; +import org.zkoss.zul.Listitem; +import org.zkoss.zul.Messagebox; import org.zkoss.zul.api.Combobox; import org.zkoss.zul.api.Datebox; import org.zkoss.zul.api.Row; @@ -142,6 +150,8 @@ public class TaskPropertiesController extends GenericForwardComposer { */ private TaskEditFormComposer taskEditFormComposer = new TaskEditFormComposer(); + private EditTaskController editTaskController; + private TaskElement currentTaskElement; private Tabpanel tabpanel; @@ -158,40 +168,71 @@ public class TaskPropertiesController extends GenericForwardComposer { private IContextWithPlannerTask currentContext; - private Row subcontract; + private Row resourceAllocationType; - private Checkbox subcontractCheckbox; + private Listbox lbResourceAllocationType; - public void init(IContextWithPlannerTask context, + private ResourceAllocationTypeEnum originalState; + + public void init(EditTaskController editTaskController, + IContextWithPlannerTask context, TaskElement taskElement) { + this.editTaskController = editTaskController; this.currentContext = context; this.currentTaskElement = taskElement; + + setOldState(null); + originalState = getResourceAllocationType(currentTaskElement); + taskEditFormComposer.init(context.getRelativeTo(), context.getTask()); updateComponentValuesForTask(); } + private void setOldState(ResourceAllocationTypeEnum state) { + lbResourceAllocationType.setVariable("oldState", state, true); + } + + private ResourceAllocationTypeEnum getOldState() { + return (ResourceAllocationTypeEnum) lbResourceAllocationType + .getVariable("oldState", true); + } + + private void setResourceAllocationType(Listbox listbox, ResourceAllocationTypeEnum value) { + setResourceAllocationType(listbox, value.toString()); + } + + private void setResourceAllocationType(Listbox listbox, String label) { + for (Iterator i = listbox.getChildren().iterator(); i.hasNext(); ) { + Listitem item = (Listitem) i.next(); + Listcell cell = (Listcell) item.getFirstChild(); + if (cell.getLabel() != null && cell.getLabel().equals(label)) { + item.setSelected(true); + } + } + } + private void updateComponentValuesForTask() { if (currentTaskElement instanceof Task) { Task task = (Task) currentTaskElement; showDurationRow(task); showStartConstraintRow(task); - showSubcontractRow(task); - } else { + showResourceAllocationTypeRow(task); + } + else { hideDurationRow(); hideStartConstraintRow(); - hideSubcontractRow(); + hideResourceAllocationTypeRow(); } hours.setValue(currentTaskElement.getWorkHours()); Util.reloadBindings(tabpanel); } - private void hideSubcontractRow() { - subcontract.setVisible(false); + private void hideResourceAllocationTypeRow() { + resourceAllocationType.setVisible(false); } - private void showSubcontractRow(Task task) { - subcontractCheckbox.setChecked(task.getSubcontractedTaskData() != null); - subcontract.setVisible(true); + private void showResourceAllocationTypeRow(Task task) { + resourceAllocationType.setVisible(true); } private void hideStartConstraintRow() { @@ -270,6 +311,34 @@ public class TaskPropertiesController extends GenericForwardComposer { constraintTypeChoosen(constraint); } }); + + lbResourceAllocationType.addEventListener(Events.ON_SELECT, new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + SelectEvent se = (SelectEvent) event; + + final ResourceAllocationTypeEnum oldState = getOldState(); + ResourceAllocationTypeEnum newState = getSelectedValue(new ArrayList(se.getSelectedItems())); + if (thereIsTransition(newState)) { + changeResourceAllocationType(oldState, newState); + } + if (oldState == null) { + setOldState(newState); + } + } + + private ResourceAllocationTypeEnum getSelectedValue(List selectedItems) { + final Listitem item = (Listitem) selectedItems.get(0); + final Listcell cell = (Listcell) item.getChildren().get(0); + return ResourceAllocationTypeEnum.asEnum(cell.getLabel()); + } + + }); + } + + private boolean thereIsTransition(ResourceAllocationTypeEnum newState) { + return getOldState() != null && !getOldState().equals(newState); } public TaskDTO getGanttTaskDTO() { @@ -293,4 +362,229 @@ public class TaskPropertiesController extends GenericForwardComposer { taskEditFormComposer.cancel(); } -} + /** + * Enum for showing type of resource assignation option list + * + * @author Diego Pino Garcia + * + */ + public enum ResourceAllocationTypeEnum { + NON_LIMITING_RESOURCES(_("Non limiting resource assignation")), + LIMITING_RESOURCES(_("Limiting resource assignation")), + SUBCONTRACT(_("Subcontract")); + + private String option; + + private ResourceAllocationTypeEnum(String option) { + this.option = option; + } + + public String toString() { + return option; + } + + public static List getOptionList() { + return Arrays.asList(values()); + } + + public static ResourceAllocationTypeEnum getDefault() { + return NON_LIMITING_RESOURCES; + } + + public static ResourceAllocationTypeEnum asEnum(String label) { + if (NON_LIMITING_RESOURCES.toString().equals(label)) { + return NON_LIMITING_RESOURCES; + } else if (LIMITING_RESOURCES.toString().equals(label)) { + return LIMITING_RESOURCES; + } else if (SUBCONTRACT.toString().equals(label)) { + return SUBCONTRACT; + } + return getDefault(); + } + + } + + public List getResourceAllocationTypeOptionList() { + return ResourceAllocationTypeEnum.getOptionList(); + } + + public ResourceAllocationTypeEnum getResourceAllocationType() { + return getResourceAllocationType(currentTaskElement); + } + + /** + * Does nothing, but it must exist for receiving selected value from listbox + * + * @param resourceAllocation + */ + public void setResourceAllocationType(ResourceAllocationTypeEnum resourceAllocation) { + + } + + public ResourceAllocationTypeEnum getResourceAllocationType(TaskElement taskElement) { + if (taskElement == null || !isTask(taskElement)) { + return null; + } + return getResourceAllocationType(asTask(currentTaskElement)); + } + + /** + * Returns type of resource allocation depending on state of task + * + * If task is subcontracted, return a SUBCONTRACT state + * If task has at least one limiting resource, returns a LIMITING RESOURCE state + * Otherwise, return default state (NON-LIMITING RESOURCE) + * + * @return + */ + public ResourceAllocationTypeEnum getResourceAllocationType(Task task) { + ResourceAllocationTypeEnum result = ResourceAllocationTypeEnum.NON_LIMITING_RESOURCES; + + if (task.isSubcontracted()) { + result = ResourceAllocationTypeEnum.SUBCONTRACT; + } + if (task.isLimiting()) { + result = ResourceAllocationTypeEnum.LIMITING_RESOURCES; + } + return result; + } + + private boolean isTask(TaskElement taskElement) { + return taskElement instanceof Task; + } + + private Task asTask(TaskElement taskElement) { + return (Task) taskElement; + } + + private void changeResourceAllocationType(ResourceAllocationTypeEnum from, ResourceAllocationTypeEnum to) { + if (from.equals(ResourceAllocationTypeEnum.NON_LIMITING_RESOURCES)) { + fromNonLimitingResource(to); + } else if (from.equals(ResourceAllocationTypeEnum.LIMITING_RESOURCES)) { + fromLimitingResource(to); + } else if (from.equals(ResourceAllocationTypeEnum.SUBCONTRACT)) { + fromSubcontract(to); + } + } + + /** + * Change state from NonLimitingResource assignation type to a new state (limiting, subcontract) + * + * @param newState + */ + private void fromNonLimitingResource(ResourceAllocationTypeEnum newState) { + if (!isTask(currentTaskElement)) { + return; + } + + Task task = asTask(currentTaskElement); + if (task.hasResourceAllocations()) { + try { + if (Messagebox.show(_("Assigned resources for this task will be deleted. Are you sure?"), + _("Warning"), Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION) == Messagebox.OK) { + task.removeAllResourceAllocations(); + setStateTo(newState); + } else { + resetStateTo(ResourceAllocationTypeEnum.NON_LIMITING_RESOURCES); + } + return; + } catch (InterruptedException e) { + + } + } + setStateTo(newState); + } + + private void setStateTo(ResourceAllocationTypeEnum state) { + setOldState(state); + editTaskController.showTabPanel(state); + } + + private void resetStateTo(ResourceAllocationTypeEnum state) { + setResourceAllocationType(lbResourceAllocationType, state); + setOldState(state); + } + + /** + * Change state from LimitingResource assignation type to a new state (non-limiting, subcontract) + * + * @param newState + */ + private void fromLimitingResource(ResourceAllocationTypeEnum newState) { + if (!isTask(currentTaskElement)) { + return; + } + + Task task = asTask(currentTaskElement); + if (task.hasResourceAllocations()) { + try { + if (Messagebox.show(_("Assigned resources for this task will be deleted. Are you sure?"), + _("Warning"), Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION) == Messagebox.OK) { + task.removeAllResourceAllocations(); + setStateTo(newState); + } else { + resetStateTo(ResourceAllocationTypeEnum.LIMITING_RESOURCES); + } + return; + } catch (InterruptedException e) { + + } + } + setStateTo(newState); + } + + /** + * Change state from Subcontract assignation type to a new state (non-limiting, limiting) + * + * @param newState + */ + private void fromSubcontract(ResourceAllocationTypeEnum newState) { + Task task = asTask(currentTaskElement); + + if (task.isSubcontracted()) { + final Date communicationDate = (task.getSubcontractedTaskData() != null) ? + task.getSubcontractedTaskData().getSubcontractCommunicationDate() + : null; + + // Notification has been sent + if (communicationDate != null) { + try { + if (Messagebox.show(_("IMPORTANT: Don't forget to communicate to subcontractor that his contract has been cancelled"), + _("Warning"), Messagebox.OK, Messagebox.EXCLAMATION) == Messagebox.OK) { + setStateTo(newState); + } else { + resetStateTo(ResourceAllocationTypeEnum.SUBCONTRACT); + } + return; + } catch (InterruptedException e) { + + } + } + } + setStateTo(newState); + } + + public boolean stateHasChanged() { + final ResourceAllocationTypeEnum currentState = getCurrentState(); + return currentState != null && !currentState.equals(getOriginalState()); + } + + public ResourceAllocationTypeEnum getOriginalState() { + return originalState; + } + + public ResourceAllocationTypeEnum getCurrentState() { + return getSelectedResourceAllocationType(); + } + + private ResourceAllocationTypeEnum getSelectedResourceAllocationType() { + final Listitem item = lbResourceAllocationType.getSelectedItem(); + if (item == null) { + return null; + } + + final Listcell cell = (Listcell) item.getChildren().get(0); + return ResourceAllocationTypeEnum.asEnum(cell.getLabel()); + } + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/IResourceSearchModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/IResourceSearchModel.java index d9dc3fcce..5f53083d2 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/IResourceSearchModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/IResourceSearchModel.java @@ -26,9 +26,7 @@ import java.util.Set; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionType; -import org.navalplanner.business.resources.entities.Machine; import org.navalplanner.business.resources.entities.Resource; -import org.navalplanner.business.resources.entities.Worker; /** * Conversation for worker search @@ -38,24 +36,14 @@ import org.navalplanner.business.resources.entities.Worker; public interface IResourceSearchModel { /** - * Returns all {@link Worker} matching by name (firstname or surname) + * Returns all resources filtering by name, criteria and limitingResource * * @param name - * @return - */ - List findResources(String name); - - /** - * Queries database for retrieving all resources that match to the - * parameters - * @param name - * matches name/NIF of {@link Worker} or name/code of - * {@link Machine} * @param criterions - * {@link Resource} that satisfy all criterions + * @param limitingResource * @return */ - List findResources(String name, List criterions); + List findResources(String name, List criteria, boolean limitingResource); /** * Returns all resources @@ -68,4 +56,19 @@ public interface IResourceSearchModel { * @return HashMap> */ Map> getCriterions(); + + /** + * Returns all limiting resources + * + * @return + */ + List getAllLimitingResources(); + + /** + * Returns all non-limiting resources + * + * @return + */ + List getAllNonLimitingResources(); + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorController.java index c81fdb6dc..bdc171487 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorController.java @@ -81,6 +81,8 @@ public class NewAllocationSelectorController extends GenericForwardComposer { private AllocationType currentAllocationType; + private boolean limitingResource = false; + public NewAllocationSelectorController() { } @@ -114,7 +116,7 @@ public class NewAllocationSelectorController extends GenericForwardComposer { listBoxResources.setItemRenderer(getListitemRenderer()); // Show all workers - refreshListBoxResources(resourceSearchModel.getAllResources()); + refreshListBoxResources(getAllResources()); allocationTypeSelector.addEventListener(Events.ON_CHECK, new EventListener() { @@ -139,6 +141,12 @@ public class NewAllocationSelectorController extends GenericForwardComposer { doInitialSelection(); } + private List getAllResources() { + return (limitingResource) ? resourceSearchModel + .getAllLimitingResources() : resourceSearchModel + .getAllNonLimitingResources(); + } + private void doInitialSelection() { currentAllocationType = AllocationType.SPECIFIC; AllocationType.SPECIFIC.doTheSelectionOn(allocationTypeSelector); @@ -187,8 +195,7 @@ public class NewAllocationSelectorController extends GenericForwardComposer { */ private void searchResources(String name, List criterions) { final List resources = resourceSearchModel.findResources( - name, - criterions); + name, criterions, limitingResource); refreshListBoxResources(resources); } @@ -226,7 +233,7 @@ public class NewAllocationSelectorController extends GenericForwardComposer { public void clearAll() { txtName.setValue(""); - refreshListBoxResources(resourceSearchModel.getAllResources()); + refreshListBoxResources(getAllResources()); criterionsTree.setModel(getCriterions()); clearSelection(listBoxResources); clearSelection(criterionsTree); @@ -420,4 +427,12 @@ public class NewAllocationSelectorController extends GenericForwardComposer { currentAllocationType.addTo(this, allocationsAdder); } + public void setLimitingResourceFilter(boolean limitingResource) { + this.limitingResource = limitingResource; + } + + public void allowSelectMultipleResources(boolean multiple) { + listBoxResources.setMultiple(multiple); + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/ResourceSearchModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/ResourceSearchModel.java index f7c454208..cbc7433b5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/ResourceSearchModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/ResourceSearchModel.java @@ -21,7 +21,6 @@ package org.navalplanner.web.resources.search; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -35,7 +34,9 @@ import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.daos.IWorkerDAO; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionType; +import org.navalplanner.business.resources.entities.Machine; import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.business.resources.entities.Worker; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -79,41 +80,50 @@ public class ResourceSearchModel implements IResourceSearchModel { @Override @Transactional(readOnly = true) - public List findResources(String name, List criterions) { - return findByNameAndCriterions(name, criterions); - } - - @Override - @Transactional(readOnly = true) - public List findResources(String name) { - return findByNameAndCriterions(name, Collections - . emptyList()); + public List findResources(String name, List criteria, boolean limitingResource) { + return findByNameAndCriterions(name, criteria, limitingResource); } private List findByNameAndCriterions(String name, - List criterions) { + List criteria, boolean limitingResource) { + final boolean emptyName = StringUtils.isEmpty(name); - if (criterions.isEmpty() && emptyName) { - return resourceDAO.list(Resource.class); + if (criteria.isEmpty() && emptyName) { + return getAllResources(limitingResource); } - Set resourcesMatchingCriterions = null; - if (!criterions.isEmpty()) { - resourcesMatchingCriterions = new HashSet(resourceDAO - .findSatisfyingCriterionsAtSomePoint(criterions)); - if (resourcesMatchingCriterions.isEmpty()) { + + Set resourcesMatchingCriteria = null; + if (!criteria.isEmpty()) { + resourcesMatchingCriteria = new HashSet(resourceDAO + .findSatisfyingAllCriterions(criteria, limitingResource)); + if (resourcesMatchingCriteria.isEmpty()) { return new ArrayList(); } } if (emptyName) { - return new ArrayList(resourcesMatchingCriterions); + return new ArrayList(resourcesMatchingCriteria); } - Set result = intersect(workerDAO.findByNameSubpartOrNifCaseInsensitive(name), - resourcesMatchingCriterions); - result.addAll(intersect(machineDAO.findByNameOrCode(name), - resourcesMatchingCriterions)); + Set result = intersect(findWorkers(name, + limitingResource), resourcesMatchingCriteria); + result.addAll(intersect(findMachines(name, limitingResource), + resourcesMatchingCriteria)); return new ArrayList(result); } + private List findWorkers(String name, boolean limitingResource) { + return workerDAO.findByNameSubpartOrNifCaseInsensitive(name, + limitingResource); + } + + private List findMachines(String name, boolean limitingResource) { + return machineDAO.findByNameOrCode(name, limitingResource); + } + + private List getAllResources(boolean limitingResource) { + return (limitingResource) ? resourceDAO.getAllLimitingResources() + : resourceDAO.getAllNonLimitingResources(); + } + private static Set intersect(List all, Set filteringBy) { if (filteringBy == null) { @@ -129,4 +139,17 @@ public class ResourceSearchModel implements IResourceSearchModel { public List getAllResources() { return resourceDAO.getResources(); } + + @Override + @Transactional(readOnly = true) + public List getAllLimitingResources() { + return resourceDAO.getAllLimitingResources(); + } + + @Override + @Transactional(readOnly = true) + public List getAllNonLimitingResources() { + return resourceDAO.getAllNonLimitingResources(); + } + } diff --git a/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul b/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul new file mode 100644 index 000000000..4fd7c938d --- /dev/null +++ b/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +