ItEr18S14CUAsignacionRecursosEspecificosAPlanificacion: Implemented functionallity to assign resources to a task from the Scheduleing view.

This commit is contained in:
Manuel Rego Casasnovas 2009-07-27 11:58:15 +02:00 committed by Javier Moran Rua
parent 886a15e3d9
commit d358e8dab9
18 changed files with 624 additions and 16 deletions

View file

@ -0,0 +1,18 @@
package org.navalplanner.business.orders.daos;
import org.navalplanner.business.common.daos.impl.GenericDaoHibernate;
import org.navalplanner.business.orders.entities.HoursGroup;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
/**
* Dao for {@link HoursGroup}
*
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
@Repository
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class HoursGroupDao extends GenericDaoHibernate<HoursGroup, Long>
implements IHoursGroupDao {
}

View file

@ -0,0 +1,13 @@
package org.navalplanner.business.orders.daos;
import org.navalplanner.business.common.daos.IGenericDao;
import org.navalplanner.business.orders.entities.HoursGroup;
/**
* Contract for {@link HoursGroupDao}
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public interface IHoursGroupDao extends IGenericDao<HoursGroup, Long> {
}

View file

@ -31,10 +31,4 @@ public class SpecificResourceAllocation extends ResourceAllocation {
this.worker = worker;
}
public void forceLoadWorker() {
if (worker != null) {
worker.getId();
}
}
}

View file

@ -65,8 +65,4 @@ public class Task extends TaskElement {
resourceAllocations.remove(resourceAllocation);
}
public void forceLoadResourceAllocations() {
resourceAllocations.size();
}
}

View file

@ -10,7 +10,7 @@
<property name="percentage" />
<many-to-one class="Task" name="task" column="TASK" not-null="true" />
<many-to-one class="Task" name="task" column="TASK" />
<joined-subclass name="SpecificResourceAllocation">
<key column="RESOURCE_ALLOCATION_ID" />

View file

@ -27,7 +27,7 @@
<joined-subclass name="Task">
<key column="TASK_ELEMENT_ID"></key>
<many-to-one name="hoursGroup" cascade="none"/>
<set name="resourceAllocations" cascade="all">
<set name="resourceAllocations" cascade="all-delete-orphan">
<key column="TASK" />
<one-to-many class="ResourceAllocation" />
</set>

View file

@ -245,4 +245,4 @@ public class TaskList extends XulElement implements AfterCompose {
}
}
}
}

View file

@ -14,6 +14,7 @@ public interface IOrderPlanningModel {
}
void createConfiguration(Order order,
ResourceAllocationController resourceAllocationController,
ConfigurationOnTransaction onTransaction);
}

View file

@ -0,0 +1,16 @@
package org.navalplanner.web.planner;
import org.navalplanner.business.planner.entities.TaskElement;
import org.zkoss.ganttz.extensions.ICommandOnTask;
/**
* Contract for {@link ResourceAllocationCommand}.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public interface IResourceAllocationCommand extends ICommandOnTask<TaskElement> {
void setResourceAllocationController(
ResourceAllocationController resourceAllocationController);
}

View file

@ -0,0 +1,106 @@
package org.navalplanner.web.planner;
import java.util.Set;
import org.navalplanner.business.planner.entities.ResourceAllocation;
import org.navalplanner.business.planner.entities.SpecificResourceAllocation;
import org.navalplanner.business.planner.entities.Task;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.Worker;
/**
* Contract for {@link Task}.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public interface IResourceAllocationModel {
/**
* Gets the current {@link Task} object.
*
* @return A {@link Task}
*/
Task getTask();
/**
* Adds a new {@link ResourceAllocation} to the current {@link Task}.
*/
void addResourceAllocation();
/**
* Removes the {@link ResourceAllocation} from the current {@link Task}.
*
* @param resourceAllocation
* The object to be removed
*/
void removeResourceAllocation(ResourceAllocation resourceAllocation);
/**
* Tries to find a {@link Worker} with the specified NIF.
*
* @param nif
* The NIF to search the {@link Worker}
* @return The {@link Worker} with this NIF or <code>null</code> if it's not
* found
*/
Worker findWorkerByNif(String nif);
/**
* Relates a {@link Worker} and a {@link Task} through a
* {@link SpecificResourceAllocation}.
*
* @param resourceAllocation
* A {@link SpecificResourceAllocation} to set the {@link Worker}
* @param worker
* A {@link Worker} for the {@link SpecificResourceAllocation}
*/
void setWorker(SpecificResourceAllocation resourceAllocation, Worker worker);
/**
* Sets the current {@link Task}, where the user is allocating resources.
*
* @param task
* A {@link Task}
*/
void setTask(Task task);
/**
* Gets the {@link Set} of {@link Criterion} of the current task.
*
* @return A {@link Set} of {@link Criterion}
*/
Set<Criterion> getCriterions();
/**
* Gets the {@link Set} of {@link ResourceAllocation} of the current task.
*
* @return A {@link Set} of {@link ResourceAllocation}
*/
Set<ResourceAllocation> getResourceAllocations();
/**
* Sets the current {@link ResourceAllocation} to be rendered.
*
* @param resourceAllocation
* The current {@link ResourceAllocation}
*/
void setResourceAllocation(ResourceAllocation resourceAllocation);
/**
* Gets the {@link Worker} of the current {@link ResourceAllocation}.
*
* @return A {@link Worker}
*/
Worker getWorker();
/**
* Checks if the {@link Worker} of the current {@link ResourceAllocation}
* satisfies the {@link Criterion} of the current {@link Task}.
*
* @return True if the {@link Worker} satisfies the {@link Criterion}
* required. Or if the current {@link Worker} is <code>null</code>.
* Or if the {@link Criterion} list is empty.
*/
boolean workerSatisfiesCriterions();
}

View file

@ -20,6 +20,13 @@ import org.zkoss.ganttz.adapters.PlannerConfiguration;
public class OrderPlanningController implements
IOrderPlanningControllerEntryPoints {
@Autowired
private ResourceAllocationController resourceAllocationController;
public ResourceAllocationController getResourceAllocationController() {
return resourceAllocationController;
}
@Autowired
private IURLHandlerRegistry urlHandlerRegistry;
@ -33,7 +40,8 @@ public class OrderPlanningController implements
@Override
public void showSchedule(Order order) {
model.createConfiguration(order, new ConfigurationOnTransaction() {
model.createConfiguration(order, resourceAllocationController,
new ConfigurationOnTransaction() {
@Override
public void use(PlannerConfiguration<TaskElement> configuration) {

View file

@ -39,17 +39,25 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel {
}
}
@Override
@Transactional(readOnly = true)
public void createConfiguration(Order order,
ResourceAllocationController resourceAllocationController,
ConfigurationOnTransaction onTransaction) {
Order orderReloaded = reload(order);
if (!orderReloaded.isSomeTaskElementScheduled())
throw new IllegalArgumentException("the order " + order
+ " must be scheduled");
PlannerConfiguration<TaskElement> configuration = createConfiguration(orderReloaded);
ISaveCommand saveCommand = getSaveCommand();
saveCommand.setState(state);
configuration.addGlobalCommand(saveCommand);
IResourceAllocationCommand resourceAllocationCommand = getResourceAllocationCommand();
resourceAllocationCommand
.setResourceAllocationController(resourceAllocationController);
configuration.addCommandOnTask(resourceAllocationCommand);
onTransaction.use(configuration);
}
@ -82,6 +90,8 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel {
protected abstract ISaveCommand getSaveCommand();
protected abstract IResourceAllocationCommand getResourceAllocationCommand();
private Order reload(Order order) {
try {
return orderService.find(order.getId());

View file

@ -0,0 +1,42 @@
package org.navalplanner.web.planner;
import org.navalplanner.business.planner.entities.Task;
import org.navalplanner.business.planner.entities.TaskElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.zkoss.ganttz.extensions.IContext;
/**
* A command that opens a window to make the resource allocation of a task.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ResourceAllocationCommand implements IResourceAllocationCommand {
private ResourceAllocationController resourceAllocationController;
public ResourceAllocationCommand() {
}
@Override
public void doAction(IContext<TaskElement> context, TaskElement task) {
if (task instanceof Task) {
this.resourceAllocationController.showWindow((Task) task);
}
}
@Override
public String getName() {
return "Resource allocation";
}
@Override
public void setResourceAllocationController(
ResourceAllocationController resourceAllocationController) {
this.resourceAllocationController = resourceAllocationController;
}
}

View file

@ -0,0 +1,197 @@
package org.navalplanner.web.planner;
import java.math.BigDecimal;
import java.util.Set;
import org.navalplanner.business.planner.entities.ResourceAllocation;
import org.navalplanner.business.planner.entities.SpecificResourceAllocation;
import org.navalplanner.business.planner.entities.Task;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.web.common.Util;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.SuspendNotAllowedException;
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.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Decimalbox;
import org.zkoss.zul.Label;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.api.Window;
/**
* Controller for {@link ResourceAllocation} view.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
@org.springframework.stereotype.Component("resourceAllocationController")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ResourceAllocationController extends GenericForwardComposer {
private IResourceAllocationModel resourceAllocationModel;
private ResourceAllocationListitemRender resourceAllocationRenderer = new ResourceAllocationListitemRender();
private Listbox resourcesList;
private Window window;
public Set<Criterion> getCriterions() {
return resourceAllocationModel.getCriterions();
}
public Set<ResourceAllocation> getResourceAllocations() {
return resourceAllocationModel.getResourceAllocations();
}
public ResourceAllocationListitemRender getResourceAllocationRenderer() {
return resourceAllocationRenderer;
}
public void addResourceAllocation() {
resourceAllocationModel.addResourceAllocation();
Util.reloadBindings(resourcesList);
}
public void removeResourceAllocation() {
Set<Listitem> selectedItems = resourcesList.getSelectedItems();
for (Listitem listitem : selectedItems) {
ResourceAllocation resourceAllocation = (ResourceAllocation) listitem
.getValue();
resourceAllocationModel.removeResourceAllocation(resourceAllocation);
}
Util.reloadBindings(resourcesList);
}
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
this.window = (Window) comp;
}
public void showWindow(Task task) {
resourceAllocationModel.setTask(task);
Util.reloadBindings(window);
try {
window.doModal();
} catch (SuspendNotAllowedException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void back() {
Set<ResourceAllocation> resourceAllocations = resourceAllocationModel.getResourceAllocations();
for (ResourceAllocation resourceAllocation : resourceAllocations) {
if (((SpecificResourceAllocation) resourceAllocation).getWorker() == null) {
throw new WrongValueException(
window.getFellow("resourcesList"),
"Worker not valid in some resource allocation");
}
}
Clients.closeErrorBox(window.getFellow("resourcesList"));
window.setVisible(false);
}
/**
* Renders every {@link ResourceAllocation} showing a form to modify its
* information.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public class ResourceAllocationListitemRender implements ListitemRenderer {
@Override
public void render(Listitem item, Object data) throws Exception {
final ResourceAllocation resourceAllocation = (ResourceAllocation) data;
item.setValue(resourceAllocation);
resourceAllocationModel.setResourceAllocation(resourceAllocation);
final Worker worker = resourceAllocationModel.getWorker();
Listcell cellResource = new Listcell();
final Textbox resourceTextbox = new Textbox();
Util.bind(
resourceTextbox, new Util.Getter<String>() {
@Override
public String get() {
if (worker == null) {
return "";
}
return worker.getNif();
}
}, new Util.Setter<String>() {
@Override
public void set(String value) {
Worker worker = resourceAllocationModel
.findWorkerByNif(value);
if (worker == null) {
throw new WrongValueException(resourceTextbox,
"Worker not found");
} else {
resourceAllocationModel
.setWorker(
(SpecificResourceAllocation) resourceAllocation,
worker);
}
}
});
resourceTextbox.addEventListener(Events.ON_CHANGE,
new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
Util.reloadBindings(resourcesList);
}
});
cellResource.appendChild(resourceTextbox);
cellResource.setParent(item);
Listcell cellPercentage = new Listcell();
cellPercentage.appendChild(Util.bind(
new Decimalbox(),
new Util.Getter<BigDecimal>() {
@Override
public BigDecimal get() {
return resourceAllocation.getPercentage();
}
}, new Util.Setter<BigDecimal>() {
@Override
public void set(BigDecimal value) {
resourceAllocation.setPercentage(value);
}
}));
cellPercentage.setParent(item);
Listcell cellMessage = new Listcell();
String message = "";
if (worker != null) {
if (!resourceAllocationModel.workerSatisfiesCriterions()) {
message = "The worker does not satisfy the criterions";
}
}
cellMessage.appendChild(new Label(message));
cellMessage.setParent(item);
}
}
}

View file

@ -0,0 +1,166 @@
package org.navalplanner.web.planner;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.orders.daos.IHoursGroupDao;
import org.navalplanner.business.orders.entities.HoursGroup;
import org.navalplanner.business.planner.daos.IResourceAllocationDAO;
import org.navalplanner.business.planner.daos.ITaskElementDao;
import org.navalplanner.business.planner.entities.ResourceAllocation;
import org.navalplanner.business.planner.entities.SpecificResourceAllocation;
import org.navalplanner.business.planner.entities.Task;
import org.navalplanner.business.resources.daos.IWorkerDao;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionCompounder;
import org.navalplanner.business.resources.entities.CriterionSatisfaction;
import org.navalplanner.business.resources.entities.ICriterion;
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;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Model for UI operations related to {@link Task}.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ResourceAllocationModel implements IResourceAllocationModel {
@Autowired
private ITaskElementDao taskElementDAO;
@Autowired
private IWorkerDao workerDAO;
@Autowired
private IHoursGroupDao hoursGroupDAO;
@Autowired
private IResourceAllocationDAO resourceAllocationDAO;
private Task task;
private ResourceAllocation resourceAllocation;
@Override
@Transactional(readOnly = true)
public void setTask(Task task) {
taskElementDAO.save(task);
task.getResourceAllocations().size();
HoursGroup hoursGroup = task.getHoursGroup();
hoursGroupDAO.save(hoursGroup);
hoursGroup.getCriterions().size();
this.task = task;
}
@Override
public Task getTask() {
return task;
}
@Override
public void addResourceAllocation() {
ResourceAllocation resourceAllocation = new SpecificResourceAllocation(
task);
task.addResourceAllocation(resourceAllocation);
}
@Override
public void removeResourceAllocation(ResourceAllocation resourceAllocation) {
task.removeResourceAllocation(resourceAllocation);
}
@Override
@Transactional(readOnly = true)
public Worker findWorkerByNif(String nif) {
try {
return workerDAO.findUniqueByNif(nif);
} catch (InstanceNotFoundException e) {
return null;
}
}
@Override
public void setWorker(SpecificResourceAllocation resourceAllocation,
Worker worker) {
resourceAllocation.setWorker(worker);
}
@Override
public Set<Criterion> getCriterions() {
if (task == null) {
return new HashSet<Criterion>();
}
return task.getHoursGroup().getCriterions();
}
@Override
public Set<ResourceAllocation> getResourceAllocations() {
if (task == null) {
return new HashSet<ResourceAllocation>();
}
return task.getResourceAllocations();
}
@Override
@Transactional(readOnly = true)
public void setResourceAllocation(ResourceAllocation resourceAllocation) {
boolean wasTransient = resourceAllocation.isTransient();
resourceAllocationDAO.save(resourceAllocation);
Worker worker = ((SpecificResourceAllocation) resourceAllocation)
.getWorker();
if (worker != null) {
workerDAO.save(worker);
Set<CriterionSatisfaction> criterionSatisfactions = worker
.getAllSatisfactions();
for (CriterionSatisfaction criterionSatisfaction : criterionSatisfactions) {
criterionSatisfaction.getCriterion().getName();
criterionSatisfaction.getCriterion().getType().getName();
}
}
if (wasTransient) {
resourceAllocation.makeTransientAgain();
}
this.resourceAllocation = resourceAllocation;
}
@Override
public Worker getWorker() {
if (resourceAllocation == null) {
return null;
}
return ((SpecificResourceAllocation) resourceAllocation).getWorker();
}
@Override
public boolean workerSatisfiesCriterions() {
Worker worker = getWorker();
if (worker == null) {
return true;
}
List<ICriterion> criterions = new ArrayList<ICriterion>(getCriterions());
if (criterions.isEmpty()) {
return true;
}
ICriterion compositedCriterion = CriterionCompounder.buildAnd(criterions)
.getResult();
return compositedCriterion.isSatisfiedBy(worker);
}
}

View file

@ -18,6 +18,7 @@
<bean class="org.navalplanner.web.planner.OrderPlanningModel" scope="prototype">
<lookup-method name="getTaskElementAdapter" bean="taskElementAdapter"/>
<lookup-method name="getSaveCommand" bean="saveCommand"/>
<lookup-method name="getResourceAllocationCommand" bean="resourceAllocationCommand"/>
</bean>
<context:component-scan base-package="org.navalplanner.web"/>

View file

@ -1,13 +1,16 @@
<?page title="Navalpro: Scheduling"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/common/layout/template_v02.zul"?>
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<?link rel="stylesheet" type="text/css" href="/planner/css/ganttzk.css"?>
<?link rel="stylesheet" type="text/css" href="/common/css/navalpro_v01.css"?>
<?link rel="stylesheet" type="text/css" href="/common/css/navalpro_zk.css"?>
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<zk>
<zscript><![CDATA[
plannerData = new org.navalplanner.web.planner.DataForPlanner();
allocationController = resourceAllocationController;
]]>
</zscript>
<!-- choose lightLoad, mediumLoad or highLoad.

View file

@ -1,5 +1,6 @@
<?page title="Navalpro: Scheduling"?>
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/common/layout/template_v02.zul"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<?link rel="stylesheet" type="text/css" href="/planner/css/ganttzk.css"?>
@ -7,6 +8,12 @@
<?link rel="stylesheet" type="text/css" href="/common/css/navalpro_zk.css"?>
<zk>
<zscript><![CDATA[
planningController = orderPlanningController;
allocationController = planningController.resourceAllocationController;
]]>
</zscript>
<planner id="planner" self="@{define(content)}">
<div id="idContextMenuTaskAssigment"></div>
</planner>
@ -35,8 +42,38 @@
<button id="ok" label=" ${c:l('task.ok')}" />
</popup>
<zscript><![CDATA[
orderPlanningController.registerPlanner(planner);
<window id="resourceAllocationWindow" visible="false" apply="${allocationController}">
<vbox>
<label value="Required criterions" />
<listbox id="requiredCriterions"
model="@{allocationController.criterions}">
<listitem self="@{each='criterion'}">
<listcell label="@{criterion.name}"></listcell>
</listitem>
</listbox>
<hbox>
<button id="addResourceAllocation" label="Add"
onClick="allocationController.addResourceAllocation();" />
<button id="removeResourceAllocation" label="Remove"
onClick="allocationController.removeResourceAllocation();" />
</hbox>
<listbox id="resourcesList"
model="@{allocationController.resourceAllocations}"
itemRenderer="@{allocationController.resourceAllocationRenderer}">
<listhead>
<listheader label="Resource" />
<listheader label="Percentage" />
<listheader label="Message" />
</listhead>
</listbox>
<button id="closeResourceAllocationWindow" label="Back"
onClick="allocationController.back();" />
</vbox>
</window>
<zscript><![CDATA[
planningController.registerPlanner(planner);
]]>
</zscript>
</zk>