ItEr15S11CUConfiguracionDeOrganizacionsDeTraballoConUnidadesTraballoItEr14S11: Added relation between HoursGroup and Criterion.

This commit is contained in:
Manuel Rego Casasnovas 2009-06-30 18:42:01 +02:00 committed by Javier Moran Rua
parent aa7eb5f9bc
commit 6e3dbd2886
19 changed files with 837 additions and 225 deletions

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.OrderElement;
/**
* Contract for {@link OrderElementDao}
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public interface IOrderElementDao extends IGenericDao<OrderElement, Long> {
}

View file

@ -0,0 +1,19 @@
package org.navalplanner.business.orders.daos;
import org.navalplanner.business.common.daos.impl.GenericDaoHibernate;
import org.navalplanner.business.orders.entities.OrderElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
/**
* Dao for {@link OrderElement}
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
@Repository
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class OrderElementDao extends GenericDaoHibernate<OrderElement, Long>
implements
IOrderElementDao {
}

View file

@ -1,13 +1,20 @@
package org.navalplanner.business.orders.entities;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.validator.NotNull;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.ICriterionType;
public class HoursGroup implements Cloneable {
private Long id;
private Long version;
public Long getId() {
return id;
}
@ -23,6 +30,8 @@ public class HoursGroup implements Cloneable {
private HoursPolicies hoursPolicy = HoursPolicies.NO_FIXED;
private Set<Criterion> criterions = new HashSet<Criterion>();
public void setWorkingHours(Integer workingHours) {
this.workingHours = workingHours;
}
@ -47,4 +56,73 @@ public class HoursGroup implements Cloneable {
return hoursPolicy;
}
public void setCriterions(Set<Criterion> criterions) {
this.criterions = criterions;
}
public Set<Criterion> getCriterions() {
return criterions;
}
public void addCriterion(Criterion criterion) {
Criterion oldCriterion = getCriterionByType(criterion.getType());
if (oldCriterion != null) {
removeCriterion(oldCriterion);
}
criterions.add(criterion);
}
public void removeCriterion(Criterion criterion) {
criterions.remove(criterion);
}
public Criterion getCriterionByType(ICriterionType<?> type) {
for (Criterion criterion : criterions) {
if (criterion.getType().equals(type.getName())) {
return criterion;
}
}
return null;
}
public Criterion getCriterionByType(String type) {
for (Criterion criterion : criterions) {
if (criterion.getType().equals(type)) {
return criterion;
}
}
return null;
}
public void removeCriterionByType(ICriterionType<?> type) {
Criterion criterion = getCriterionByType(type);
if (criterion != null) {
removeCriterion(criterion);
}
}
public void removeCriterionByType(String type) {
Criterion criterion = getCriterionByType(type);
if (criterion != null) {
removeCriterion(criterion);
}
}
public void forceLoadCriterions() {
criterions.size();
}
public void makeTransientAgain() {
// FIXME Review reattachment
id = null;
version = null;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

View file

@ -6,7 +6,10 @@ import java.util.List;
import org.hibernate.validator.NotNull;
public abstract class OrderElement {
private long id;
private Long id;
private Long version;
@NotNull
private String name;
@ -92,4 +95,20 @@ public abstract class OrderElement {
public abstract void forceLoadHourGroups();
public abstract void forceLoadHourGroupsCriterions();
public void makeTransientAgain() {
// FIXME Review reattachment
id = null;
version = null;
for (HoursGroup hoursGroup : getHoursGroups()) {
hoursGroup.makeTransientAgain();
}
}
public boolean isTransient() {
// FIXME Review reattachment
return id == null;
}
}

View file

@ -19,7 +19,6 @@ public class OrderLine extends OrderElement {
@Override
public List<OrderElement> getChildren() {
// FIXME Shouldn't return null?
return new ArrayList<OrderElement>();
}
@ -44,14 +43,6 @@ public class OrderLine extends OrderElement {
return new ArrayList<HoursGroup>(hoursGroups);
}
@Override
public void forceLoadHourGroups() {
for (HoursGroup hoursGroup : hoursGroups) {
hoursGroup.getWorkingHours();
}
}
public void addHoursGroup(HoursGroup hoursGroup) {
hoursGroups.add(hoursGroup);
recalculatePercentages(hoursGroups);
@ -88,7 +79,6 @@ public class OrderLine extends OrderElement {
} else {
if (!isTotalHoursValid(workHours)) {
// FIXME change exception type
throw new IllegalArgumentException(
"\"workHours\" value is not valid, taking into "
+ "account the current list of HoursGroup");
@ -203,7 +193,7 @@ public class OrderLine extends OrderElement {
* The desired value
* @return true if the value is valid
*/
private boolean isTotalHoursValid(Integer total) {
public boolean isTotalHoursValid(Integer total) {
Integer newTotal = 0;
@ -291,4 +281,28 @@ public class OrderLine extends OrderElement {
}
}
/**
* Re-calculates the percentages in the {@link HoursGroup} set of the
* current {@link OrderLine}, without modify the {@link HoursGroup} with
* policy FIXED_PERCENTAGE.
*
*/
public void recalculatePercentages() {
recalculatePercentages(hoursGroups);
}
@Override
public void forceLoadHourGroups() {
for (HoursGroup hoursGroup : hoursGroups) {
hoursGroup.getWorkingHours();
}
}
@Override
public void forceLoadHourGroupsCriterions() {
for (HoursGroup hoursGroup : hoursGroups) {
hoursGroup.forceLoadCriterions();
}
}
}

View file

@ -66,13 +66,6 @@ public class OrderLineGroup extends OrderElement implements IOrderLineGroup {
return result;
}
@Override
public void forceLoadHourGroups() {
for (OrderElement orderElement : children) {
orderElement.forceLoadHourGroups();
}
}
@Override
public List<HoursGroup> getHoursGroups() {
List<HoursGroup> hoursGroups = new ArrayList<HoursGroup>();
@ -82,4 +75,18 @@ public class OrderLineGroup extends OrderElement implements IOrderLineGroup {
return hoursGroups;
}
@Override
public void forceLoadHourGroups() {
for (OrderElement orderElement : children) {
orderElement.forceLoadHourGroups();
}
}
@Override
public void forceLoadHourGroupsCriterions() {
for (OrderElement orderElement : children) {
orderElement.forceLoadHourGroupsCriterions();
}
}
}

View file

@ -5,7 +5,6 @@ import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.transaction.RollbackException;
import org.apache.commons.lang.Validate;
import org.hibernate.validator.InvalidValue;
import org.navalplanner.business.common.OnTransaction;

View file

@ -23,6 +23,7 @@
<id name="id" type="long" access="field">
<generator class="native" />
</id>
<version name="version" access="field" type="long" />
<property name="name" access="field" />
<property name="initDate" access="field" />
@ -54,10 +55,18 @@
<id name="id" type="long" access="field">
<generator class="native" />
</id>
<version name="version" access="field" type="long" />
<property name="workingHours" access="field" />
<property name="percentage" access="field" />
<property name="hoursPolicy" access="field" />
<set name="criterions" table="CriterionHoursGroup"
access="field" inverse="false">
<key column="hoursGroupId" not-null="false" />
<many-to-many column="criterionId"
class="org.navalplanner.business.resources.entities.Criterion" />
</set>
</class>
</hibernate-mapping>

View file

@ -31,6 +31,7 @@
<property name="name" access="field"/>
<property name="type" access="field"/>
<property access="field" name="active"/>
</class>
<class name="CriterionSatisfaction">

View file

@ -9,7 +9,11 @@ import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONF
import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.navalplanner.business.common.OnTransaction;
@ -18,9 +22,11 @@ import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.orders.entities.HoursGroup;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.orders.entities.OrderLineGroup;
import org.navalplanner.business.orders.entities.OrderLine;
import org.navalplanner.business.orders.entities.OrderLineGroup;
import org.navalplanner.business.orders.services.IOrderService;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.services.CriterionService;
import org.navalplanner.business.test.resources.daos.CriterionSatisfactionDAOTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.NotTransactional;
@ -51,6 +57,16 @@ public class OrderServiceTest {
@Autowired
private IOrderService orderService;
@Autowired
private CriterionService criterionService;
@Autowired
private SessionFactory sessionFactory;
private Session getSession() {
return sessionFactory.getCurrentSession();
}
@Test
public void testCreation() throws ValidationException {
Order order = createValidOrder();
@ -184,4 +200,62 @@ public class OrderServiceTest {
}
});
}
@Test
@NotTransactional
public void testManyToManyHoursGroupCriterionMapping() throws Exception {
final Order order = createValidOrder();
OrderElement orderElement = new OrderLine();
orderElement.setName("Order element");
order.add(orderElement);
HoursGroup hoursGroup = new HoursGroup();
hoursGroup.setWorkingHours(10);
HoursGroup hoursGroup2 = new HoursGroup();
hoursGroup2.setWorkingHours(5);
((OrderLine) orderElement).addHoursGroup(hoursGroup);
((OrderLine) orderElement).addHoursGroup(hoursGroup2);
Criterion criterion = Criterion.withNameAndType("Test"
+ UUID.randomUUID().toString(), "test");
criterionService.save(criterion);
hoursGroup.addCriterion(criterion);
hoursGroup2.addCriterion(criterion);
orderService.save(order);
orderService.onTransaction(new OnTransaction<Void>() {
@Override
public Void execute() {
try {
Order reloaded = orderService.find(order.getId());
List<OrderElement> orderElements = reloaded
.getOrderElements();
assertThat(orderElements.size(), equalTo(1));
List<HoursGroup> hoursGroups = orderElements.get(0)
.getHoursGroups();
assertThat(hoursGroups.size(), equalTo(2));
Set<Criterion> criterions = hoursGroups.get(0)
.getCriterions();
assertThat(criterions.size(), equalTo(1));
Criterion criterion = criterions.iterator().next();
assertThat(criterion.getType(), equalTo("test"));
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
return null;
}
});
}
}

View file

@ -0,0 +1,21 @@
package org.navalplanner.web.orders;
import java.util.List;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.ICriterionType;
public interface IOrderElementModel {
public OrderElement getOrderElement();
public void setCurrent(OrderElement orderElement);
public List<ICriterionType<?>> getCriterionTypes();
public ICriterionType<?> getCriterionTypeByName(String name);
public List<Criterion> getCriterionsFor(ICriterionType<?> type);
}

View file

@ -5,6 +5,7 @@ import java.util.List;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.orders.entities.IOrderLineGroup;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.orders.entities.OrderElement;
/**
* Contract for {@link OrderModel}<br />
@ -26,6 +27,8 @@ public interface IOrderModel {
void prepareForRemove(Order order);
OrderElementModel getOrderElementTreeModel();
OrderElementTreeModel getOrderElementTreeModel();
IOrderElementModel getOrderElementModel(OrderElement orderElement);
}

View file

@ -1,6 +1,8 @@
package org.navalplanner.web.orders;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -10,20 +12,25 @@ import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.orders.entities.OrderLine;
import org.navalplanner.business.orders.entities.OrderLineGroup;
import org.navalplanner.business.orders.entities.HoursGroup.HoursPolicies;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.ICriterionType;
import org.navalplanner.web.common.Util;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Decimalbox;
import org.zkoss.zul.Intbox;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Popup;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.api.Listhead;
/**
* Controller for {@link OrderElement} view of {@link Order} entities <br />
@ -32,10 +39,7 @@ import org.zkoss.zul.Textbox;
*/
public class OrderElementController extends GenericForwardComposer {
/**
* {@link OrderElement} that is managed
*/
private OrderElement orderElement;
private IOrderElementModel model;
/**
* {@link Popup} where {@link OrderElement} edition form is showed
@ -57,9 +61,14 @@ public class OrderElementController extends GenericForwardComposer {
*/
private Listbox hoursGroupsListbox;
private Set<ICriterionType<?>> selectedCriterionTypes = new HashSet<ICriterionType<?>>();
public OrderElement getOrderElement() {
return orderElement;
if (model == null) {
return new OrderLine();
}
return model.getOrderElement();
}
public List<HoursGroup> getHoursGroupsModel() {
@ -86,15 +95,18 @@ public class OrderElementController extends GenericForwardComposer {
* @param orderElement
* The {@link OrderElement} to be edited
*/
public void openPopup(OrderElement orderElement) {
this.orderElement = orderElement;
public void openPopup(IOrderElementModel model) {
this.model = model;
final OrderElement orderElement = model.getOrderElement();
this.hoursGroupsModel = orderElement.getHoursGroups();
// If is a container
if (orderElement instanceof OrderLineGroup) {
// Disable fields just used in the OrderLine
((Textbox) popup.getFellow("totalHours")).setDisabled(true);
((Intbox) popup.getFellow("totalHours")).setDisabled(true);
// Hide not needed buttons
popup.getFellow("manageCriterions").setVisible(false);
@ -102,17 +114,43 @@ public class OrderElementController extends GenericForwardComposer {
popup.getFellow("deleteHoursGroup").setVisible(false);
} else {
// Enable fields just used in the OrderLine
((Textbox) popup.getFellow("totalHours")).setDisabled(false);
((Intbox) popup.getFellow("totalHours")).setDisabled(false);
// Show needed buttons
popup.getFellow("manageCriterions").setVisible(true);
popup.getFellow("addHoursGroup").setVisible(true);
popup.getFellow("deleteHoursGroup").setVisible(true);
// Add EventListener to reload the popup when the value change
popup.getFellow("totalHours").addEventListener(Events.ON_CHANGE,
new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
Util.reloadBindings(popup);
}
});
((Intbox) popup.getFellow("totalHours"))
.setConstraint(new Constraint() {
@Override
public void validate(Component comp, Object value)
throws WrongValueException {
if (!((OrderLine) orderElement)
.isTotalHoursValid((Integer) value)) {
throw new WrongValueException(comp,
"Value is not valid, taking into account "
+ "the current list of HoursGroup");
}
}
});
}
Util.reloadBindings(popup);
popup.open(popup.getParent(), "start-after");
reloadSelectedCriterionTypes();
}
/**
@ -139,6 +177,8 @@ public class OrderElementController extends GenericForwardComposer {
public void addHoursGroup() {
HoursGroup hoursGroup = new HoursGroup();
OrderElement orderElement = getOrderElement();
((OrderLine) orderElement).addHoursGroup(hoursGroup);
this.hoursGroupsModel = orderElement.getHoursGroups();
@ -153,6 +193,9 @@ public class OrderElementController extends GenericForwardComposer {
*/
public void deleteHoursGroups() {
Set<Listitem> selectedItems = hoursGroupsListbox.getSelectedItems();
OrderElement orderElement = getOrderElement();
for (Listitem item : selectedItems) {
((OrderLine) orderElement).deleteHoursGroup((HoursGroup) item
.getValue());
@ -162,6 +205,76 @@ public class OrderElementController extends GenericForwardComposer {
Util.reloadBindings(popup);
}
public void manageCriterions() {
Component selectCriterions = popup.getFellow("selectCriterions");
if (selectCriterions.isVisible()) {
selectCriterions.setVisible(false);
} else {
reloadSelectedCriterionTypes();
selectCriterions.setVisible(true);
}
Util.reloadBindings(selectCriterions);
}
public List<ICriterionType<?>> getCriterionTypes() {
if (model == null) {
return new ArrayList<ICriterionType<?>>();
}
List<ICriterionType<?>> list = model.getCriterionTypes();
list.removeAll(getSelectedCriterionTypes());
return list;
}
public Set<ICriterionType<?>> getSelectedCriterionTypes() {
return selectedCriterionTypes;
}
public void reloadSelectedCriterionTypes() {
OrderElement orderElement = getOrderElement();
if (orderElement == null) {
selectedCriterionTypes = new HashSet<ICriterionType<?>>();
} else {
Set<ICriterionType<?>> criterionTypes = new HashSet<ICriterionType<?>>();
for (HoursGroup hoursGroup : orderElement.getHoursGroups()) {
Set<Criterion> criterions = hoursGroup.getCriterions();
for (Criterion criterion : criterions) {
String type = criterion.getType();
criterionTypes.add(model.getCriterionTypeByName(type));
}
}
selectedCriterionTypes = criterionTypes;
}
}
public void assignCriterions(Set<Listitem> selectedItems) {
for (Listitem listitem : selectedItems) {
ICriterionType<?> value = (ICriterionType<?>) listitem.getValue();
selectedCriterionTypes.add(value);
}
Util.reloadBindings(popup);
}
public void unassignCriterions(Set<Listitem> selectedItems) {
for (Listitem listitem : selectedItems) {
ICriterionType<?> value = (ICriterionType<?>) listitem.getValue();
selectedCriterionTypes.remove(value);
removeCriterionsFromHoursGroup(value);
}
Util.reloadBindings(popup);
}
private void removeCriterionsFromHoursGroup(ICriterionType<?> type) {
OrderElement orderElement = getOrderElement();
for (HoursGroup hoursGroup : orderElement.getHoursGroups()) {
hoursGroup.removeCriterionByType(type);
}
}
/**
* Represents every {@link HoursGroup} with an edition form if needed
*
@ -173,6 +286,8 @@ public class OrderElementController extends GenericForwardComposer {
public void render(Listitem item, Object data) throws Exception {
final HoursGroup hoursGroup = (HoursGroup) data;
hoursGroup.getCriterions();
item.setValue(hoursGroup);
Listcell cellWorkingHours = new Listcell();
@ -198,7 +313,7 @@ public class OrderElementController extends GenericForwardComposer {
decimalBox.setScale(2);
// If is a container
if (orderElement instanceof OrderLineGroup) {
if (getOrderElement() instanceof OrderLineGroup) {
// Just getters are needed
// Working hours
@ -244,6 +359,18 @@ public class OrderElementController extends GenericForwardComposer {
}
});
// Add EventListener to reload the popup when the value change
workingHours.addEventListener(Events.ON_CHANGE,
new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
Util.reloadBindings(popup);
((OrderLine) getOrderElement())
.recalculatePercentages();
}
});
final Decimalbox percentage = Util.bind(decimalBox,
new Util.Getter<BigDecimal>() {
@ -259,6 +386,18 @@ public class OrderElementController extends GenericForwardComposer {
}
});
// Add EventListener to reload the popup when the value change
percentage.addEventListener(Events.ON_CHANGE,
new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
Util.reloadBindings(popup);
((OrderLine) getOrderElement())
.recalculatePercentages();
}
});
// Hours policy
hoursPolicyListBox.setSelectedIndex(hoursGroup.getHoursPolicy()
.ordinal());
@ -286,6 +425,69 @@ public class OrderElementController extends GenericForwardComposer {
cellPercentage.appendChild(percentage);
cellHoursPolicy.appendChild(hoursPolicyListBox);
// For each ICriterionType selected
for (ICriterionType<?> criterionType : getSelectedCriterionTypes()) {
Listcell cellCriterion = new Listcell();
cellCriterion.setParent(item);
// Add a new column on the HoursGroup table
Listhead header = ((Listbox) item.getParent())
.getListheadApi();
Listheader headerCriterion = new Listheader();
headerCriterion.setLabel(criterionType.getName());
headerCriterion.setParent(header);
// Add a new Listbox for each ICriterionType
final Listbox criterionListbox = new Listbox();
criterionListbox.setRows(1);
criterionListbox.setMold("select");
// Add an empty option to remove a Criterion
Listitem emptyListitem = new Listitem();
emptyListitem.setParent(criterionListbox);
// Get the Criterion of the current type in the HoursGroup
final Criterion criterionHoursGroup = hoursGroup
.getCriterionByType(criterionType);
// For each possible Criterion of the current type
for (Criterion criterion : model
.getCriterionsFor(criterionType)) {
// Add the Criterion option
Listitem listitem = new Listitem();
listitem.setValue(criterion);
listitem.setLabel(criterion.getName());
listitem.setParent(criterionListbox);
// Check if it matches with the HoursGroup criterion
if ((criterionHoursGroup != null)
&& (criterionHoursGroup.getName()
.equals(criterion.getName()))) {
// Mark as selected
criterionListbox.setSelectedItem(listitem);
}
}
// Add operation for Criterion selection
criterionListbox.addEventListener(Events.ON_SELECT,
new EventListener() {
@Override
public void onEvent(Event event)
throws Exception {
Criterion criterion = (Criterion) criterionListbox
.getSelectedItem().getValue();
if (criterion == null) {
hoursGroup
.removeCriterion(criterionHoursGroup);
} else {
hoursGroup.addCriterion(criterion);
}
}
});
cellCriterion.appendChild(criterionListbox);
}
}
}

View file

@ -1,201 +1,85 @@
package org.navalplanner.web.orders;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.navalplanner.business.orders.entities.IOrderLineGroup;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.orders.daos.IOrderElementDao;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.orders.entities.OrderLine;
import org.navalplanner.business.orders.entities.OrderLineGroup;
import org.zkoss.zul.SimpleTreeModel;
import org.zkoss.zul.SimpleTreeNode;
import org.navalplanner.business.resources.bootstrap.ICriterionsBootstrap;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.ICriterionType;
import org.navalplanner.business.resources.services.CriterionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
/**
* Model for a the {@link OrderElement} tree for a {@link Order} <br />
*
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
public class OrderElementModel extends SimpleTreeModel {
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class OrderElementModel implements IOrderElementModel {
private static List<SimpleTreeNode> asNodes(List<OrderElement> orderElements) {
ArrayList<SimpleTreeNode> result = new ArrayList<SimpleTreeNode>();
for (OrderElement orderElement : orderElements) {
result.add(asNode(orderElement));
private OrderElement orderElement;
@Autowired
private IOrderElementDao orderElementDao;
@Autowired
private ICriterionsBootstrap criterionsBootstrap;
@Autowired
private CriterionService criterionService;
private Map<String, ICriterionType<?>> mapCriterionTypes = new HashMap<String, ICriterionType<?>>();
@Override
public OrderElement getOrderElement() {
return orderElement;
}
@Override
@Transactional(readOnly = true)
public void setCurrent(OrderElement orderElement) {
// FIXME Review reattachment
boolean wasTransient = orderElement.isTransient();
orderElementDao.save(orderElement);
orderElement.forceLoadHourGroupsCriterions();
if (wasTransient) {
orderElement.makeTransientAgain();
}
return result;
this.orderElement = orderElement;
}
private static SimpleTreeNode asNode(OrderElement orderElement) {
orderElement.forceLoadHourGroups();
return new SimpleTreeNode(orderElement, asNodes(orderElement.getChildren()));
}
@Override
public List<ICriterionType<?>> getCriterionTypes() {
List<ICriterionType<?>> criterionTypes = criterionsBootstrap
.getTypes();
private static SimpleTreeNode createRootNodeAndDescendants(
Order order) {
return new SimpleTreeNode(order, asNodes(order.getOrderElements()));
}
public OrderElementModel(Order order) {
super(createRootNodeAndDescendants(order));
}
public void reloadFromOrder() {
Order root = getRootAsOrder();
SimpleTreeNode rootAsNode = getRootAsNode();
rootAsNode.getChildren().clear();
rootAsNode.getChildren().addAll(asNodes(root.getOrderElements()));
}
public void addOrderElement() {
addOrderElementAtImpl(getRootAsNode());
reloadFromOrder();
}
private OrderElement createNewOrderElement() {
OrderElement newOrderElement = new OrderLine();
newOrderElement.setName("New Order Element");
return newOrderElement;
}
public void addOrderElementAt(SimpleTreeNode node) {
addOrderElementAtImpl(node);
reloadFromOrder();
}
private void addOrderElementAtImpl(SimpleTreeNode node) {
addOrderElementAtImpl(node, createNewOrderElement());
}
private void addOrderElementAtImpl(SimpleTreeNode node, OrderElement orderElement) {
addOrderElementAtImpl(node, orderElement, node.getChildCount());
}
private void addOrderElementAtImpl(SimpleTreeNode destinationNode, OrderElement orderElement,
int position) {
IOrderLineGroup container = turnIntoContainerIfNeeded(destinationNode);
container.add(position, orderElement);
}
private IOrderLineGroup turnIntoContainerIfNeeded(
SimpleTreeNode selectedForTurningIntoContainer) {
IOrderLineGroup parentContainer = asOrderLineGroup(getParent(selectedForTurningIntoContainer));
if (selectedForTurningIntoContainer.getData() instanceof IOrderLineGroup)
return (IOrderLineGroup) selectedForTurningIntoContainer
.getData();
OrderElement toBeTurned = asOrderLine(selectedForTurningIntoContainer);
OrderLineGroup asContainer = toBeTurned.asContainer();
parentContainer.replace(toBeTurned, asContainer);
return asContainer;
}
private SimpleTreeNode getParent(SimpleTreeNode node) {
int[] position = getPath(node);
SimpleTreeNode current = getRootAsNode();
SimpleTreeNode[] path = new SimpleTreeNode[position.length];
for (int i = 0; i < position.length; i++) {
path[i] = (SimpleTreeNode) current.getChildAt(position[i]);
current = path[i];
}
int parentOfLast = path.length - 2;
if (parentOfLast >= 0)
return path[parentOfLast];
else
return getRootAsNode();
}
public List<SimpleTreeNode> getParents(SimpleTreeNode node) {
List<SimpleTreeNode> parents = new ArrayList<SimpleTreeNode>();
SimpleTreeNode current = node;
while (!current.equals(getRootAsNode())) {
current = getParent(current);
parents.add(current);
if (mapCriterionTypes.isEmpty()) {
for (ICriterionType<?> criterionType : criterionTypes) {
mapCriterionTypes.put(criterionType.getName(), criterionType);
}
}
return parents;
return criterionTypes;
}
public void indent(SimpleTreeNode nodeToIndent) {
SimpleTreeNode parentOfSelected = getParent(nodeToIndent);
int position = parentOfSelected.getChildren().indexOf(nodeToIndent);
if (position == 0) {
return;
@Override
public ICriterionType<?> getCriterionTypeByName(String name) {
if (mapCriterionTypes.isEmpty()) {
for (ICriterionType<?> criterionType : criterionsBootstrap
.getTypes()) {
mapCriterionTypes.put(criterionType.getName(), criterionType);
}
}
SimpleTreeNode destination = (SimpleTreeNode) parentOfSelected
.getChildren().get(position - 1);
moveImpl(nodeToIndent, destination, destination.getChildCount());
reloadFromOrder();
return mapCriterionTypes.get(name);
}
public void unindent(SimpleTreeNode nodeToUnindent) {
SimpleTreeNode parent = getParent(nodeToUnindent);
if (getRootAsNode() == parent) {
return;
}
SimpleTreeNode destination = getParent(parent);
moveImpl(nodeToUnindent, destination, destination.getChildren()
.indexOf(parent) + 1);
reloadFromOrder();
@Override
public List<Criterion> getCriterionsFor(ICriterionType<?> type) {
return (List<Criterion>) criterionService.getCriterionsFor(type);
}
public void move(SimpleTreeNode toBeMoved, SimpleTreeNode destination) {
moveImpl(toBeMoved, destination, destination.getChildCount());
reloadFromOrder();
}
private void moveImpl(SimpleTreeNode toBeMoved, SimpleTreeNode destination,
int position) {
if (destination.getChildren().contains(toBeMoved)) {
return;// it's already moved
}
removeNodeImpl(toBeMoved);
addOrderElementAtImpl(destination, asOrderLine(toBeMoved), position);
}
public int[] getPath(SimpleTreeNode destination) {
int[] path = getPath(getRootAsNode(), destination);
return path;
}
public void up(SimpleTreeNode node) {
IOrderLineGroup orderLineGroup = asOrderLineGroup(getParent(node));
orderLineGroup.up(asOrderLine(node));
reloadFromOrder();
}
public void down(SimpleTreeNode node) {
IOrderLineGroup orderLineGroup = asOrderLineGroup(getParent(node));
orderLineGroup.down(asOrderLine(node));
reloadFromOrder();
}
private Order getRootAsOrder() {
return (Order) getRootAsNode().getData();
}
private static OrderElement asOrderLine(SimpleTreeNode node) {
return (OrderElement) node.getData();
}
private static IOrderLineGroup asOrderLineGroup(SimpleTreeNode node) {
return (IOrderLineGroup) node.getData();
}
private SimpleTreeNode getRootAsNode() {
return (SimpleTreeNode) getRoot();
}
public void removeNode(SimpleTreeNode value) {
removeNodeImpl(value);
reloadFromOrder();
}
private void removeNodeImpl(SimpleTreeNode value) {
if (value == getRootAsNode())
return;
IOrderLineGroup orderLineGroup = asOrderLineGroup(getParent(value));
orderLineGroup.remove(asOrderLine(value));
}
}
}

View file

@ -13,11 +13,13 @@ import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.orders.entities.OrderLine;
import org.navalplanner.web.common.Util;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.DropEvent;
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.GenericForwardComposer;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Intbox;
import org.zkoss.zul.SimpleTreeNode;
@ -64,7 +66,7 @@ public class OrderElementTreeController extends GenericForwardComposer {
}
}
public OrderElementModel getOrderElementTreeModel() {
public OrderElementTreeModel getOrderElementTreeModel() {
return orderModel.getOrderElementTreeModel();
}
@ -218,7 +220,7 @@ public class OrderElementTreeController extends GenericForwardComposer {
map.put(t, intboxHours);
if (orderElement instanceof OrderLine) {
// If it's a leaf hours cell is editable
cellForHours.appendChild(Util.bind(intboxHours,
Intbox intbox = Util.bind(intboxHours,
new Util.Getter<Integer>() {
@Override
@ -245,7 +247,23 @@ public class OrderElementTreeController extends GenericForwardComposer {
.getWorkHours());
}
}
}));
});
// Checking hours value
intbox.setConstraint(new Constraint() {
@Override
public void validate(Component comp, Object value)
throws WrongValueException {
if (!((OrderLine) orderElement)
.isTotalHoursValid((Integer) value)) {
throw new WrongValueException(comp,
"Value is not valid, taking into account "
+ "the current list of HoursGroup");
}
}
});
cellForHours.appendChild(intbox);
} else {
// If it's a container hours cell is not editable
cellForHours.appendChild(Util.bind(intboxHours,
@ -326,7 +344,9 @@ public class OrderElementTreeController extends GenericForwardComposer {
@Override
public void onEvent(Event event) throws Exception {
orderElementController.openPopup(orderElement);
IOrderElementModel model = orderModel
.getOrderElementModel(orderElement);
orderElementController.openPopup(model);
}
});

View file

@ -0,0 +1,201 @@
package org.navalplanner.web.orders;
import java.util.ArrayList;
import java.util.List;
import org.navalplanner.business.orders.entities.IOrderLineGroup;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.orders.entities.OrderLine;
import org.navalplanner.business.orders.entities.OrderLineGroup;
import org.zkoss.zul.SimpleTreeModel;
import org.zkoss.zul.SimpleTreeNode;
/**
* Model for a the {@link OrderElement} tree for a {@link Order} <br />
*
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
public class OrderElementTreeModel extends SimpleTreeModel {
private static List<SimpleTreeNode> asNodes(List<OrderElement> orderElements) {
ArrayList<SimpleTreeNode> result = new ArrayList<SimpleTreeNode>();
for (OrderElement orderElement : orderElements) {
result.add(asNode(orderElement));
}
return result;
}
private static SimpleTreeNode asNode(OrderElement orderElement) {
orderElement.forceLoadHourGroups();
return new SimpleTreeNode(orderElement, asNodes(orderElement.getChildren()));
}
private static SimpleTreeNode createRootNodeAndDescendants(
Order order) {
return new SimpleTreeNode(order, asNodes(order.getOrderElements()));
}
public OrderElementTreeModel(Order order) {
super(createRootNodeAndDescendants(order));
}
public void reloadFromOrder() {
Order root = getRootAsOrder();
SimpleTreeNode rootAsNode = getRootAsNode();
rootAsNode.getChildren().clear();
rootAsNode.getChildren().addAll(asNodes(root.getOrderElements()));
}
public void addOrderElement() {
addOrderElementAtImpl(getRootAsNode());
reloadFromOrder();
}
private OrderElement createNewOrderElement() {
OrderElement newOrderElement = new OrderLine();
newOrderElement.setName("New Order Element");
return newOrderElement;
}
public void addOrderElementAt(SimpleTreeNode node) {
addOrderElementAtImpl(node);
reloadFromOrder();
}
private void addOrderElementAtImpl(SimpleTreeNode node) {
addOrderElementAtImpl(node, createNewOrderElement());
}
private void addOrderElementAtImpl(SimpleTreeNode node, OrderElement orderElement) {
addOrderElementAtImpl(node, orderElement, node.getChildCount());
}
private void addOrderElementAtImpl(SimpleTreeNode destinationNode, OrderElement orderElement,
int position) {
IOrderLineGroup container = turnIntoContainerIfNeeded(destinationNode);
container.add(position, orderElement);
}
private IOrderLineGroup turnIntoContainerIfNeeded(
SimpleTreeNode selectedForTurningIntoContainer) {
IOrderLineGroup parentContainer = asOrderLineGroup(getParent(selectedForTurningIntoContainer));
if (selectedForTurningIntoContainer.getData() instanceof IOrderLineGroup)
return (IOrderLineGroup) selectedForTurningIntoContainer
.getData();
OrderElement toBeTurned = asOrderLine(selectedForTurningIntoContainer);
OrderLineGroup asContainer = toBeTurned.asContainer();
parentContainer.replace(toBeTurned, asContainer);
return asContainer;
}
private SimpleTreeNode getParent(SimpleTreeNode node) {
int[] position = getPath(node);
SimpleTreeNode current = getRootAsNode();
SimpleTreeNode[] path = new SimpleTreeNode[position.length];
for (int i = 0; i < position.length; i++) {
path[i] = (SimpleTreeNode) current.getChildAt(position[i]);
current = path[i];
}
int parentOfLast = path.length - 2;
if (parentOfLast >= 0)
return path[parentOfLast];
else
return getRootAsNode();
}
public List<SimpleTreeNode> getParents(SimpleTreeNode node) {
List<SimpleTreeNode> parents = new ArrayList<SimpleTreeNode>();
SimpleTreeNode current = node;
while (!current.equals(getRootAsNode())) {
current = getParent(current);
parents.add(current);
}
return parents;
}
public void indent(SimpleTreeNode nodeToIndent) {
SimpleTreeNode parentOfSelected = getParent(nodeToIndent);
int position = parentOfSelected.getChildren().indexOf(nodeToIndent);
if (position == 0) {
return;
}
SimpleTreeNode destination = (SimpleTreeNode) parentOfSelected
.getChildren().get(position - 1);
moveImpl(nodeToIndent, destination, destination.getChildCount());
reloadFromOrder();
}
public void unindent(SimpleTreeNode nodeToUnindent) {
SimpleTreeNode parent = getParent(nodeToUnindent);
if (getRootAsNode() == parent) {
return;
}
SimpleTreeNode destination = getParent(parent);
moveImpl(nodeToUnindent, destination, destination.getChildren()
.indexOf(parent) + 1);
reloadFromOrder();
}
public void move(SimpleTreeNode toBeMoved, SimpleTreeNode destination) {
moveImpl(toBeMoved, destination, destination.getChildCount());
reloadFromOrder();
}
private void moveImpl(SimpleTreeNode toBeMoved, SimpleTreeNode destination,
int position) {
if (destination.getChildren().contains(toBeMoved)) {
return;// it's already moved
}
removeNodeImpl(toBeMoved);
addOrderElementAtImpl(destination, asOrderLine(toBeMoved), position);
}
public int[] getPath(SimpleTreeNode destination) {
int[] path = getPath(getRootAsNode(), destination);
return path;
}
public void up(SimpleTreeNode node) {
IOrderLineGroup orderLineGroup = asOrderLineGroup(getParent(node));
orderLineGroup.up(asOrderLine(node));
reloadFromOrder();
}
public void down(SimpleTreeNode node) {
IOrderLineGroup orderLineGroup = asOrderLineGroup(getParent(node));
orderLineGroup.down(asOrderLine(node));
reloadFromOrder();
}
private Order getRootAsOrder() {
return (Order) getRootAsNode().getData();
}
private static OrderElement asOrderLine(SimpleTreeNode node) {
return (OrderElement) node.getData();
}
private static IOrderLineGroup asOrderLineGroup(SimpleTreeNode node) {
return (IOrderLineGroup) node.getData();
}
private SimpleTreeNode getRootAsNode() {
return (SimpleTreeNode) getRoot();
}
public void removeNode(SimpleTreeNode value) {
removeNodeImpl(value);
reloadFromOrder();
}
private void removeNodeImpl(SimpleTreeNode value) {
if (value == getRootAsNode())
return;
IOrderLineGroup orderLineGroup = asOrderLineGroup(getParent(value));
orderLineGroup.remove(asOrderLine(value));
}
}

View file

@ -10,6 +10,7 @@ import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.orders.entities.IOrderLineGroup;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.orders.services.IOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
@ -32,7 +33,10 @@ public class OrderModel implements IOrderModel {
private ClassValidator<Order> orderValidator = new ClassValidator<Order>(
Order.class);
private OrderElementModel orderElementTreeModel;
private OrderElementTreeModel orderElementTreeModel;
@Autowired
private IOrderElementModel orderElementModel;
@Autowired
public OrderModel(IOrderService orderService) {
@ -52,7 +56,7 @@ public class OrderModel implements IOrderModel {
Validate.notNull(order);
try {
this.order = orderService.find(order.getId());
this.orderElementTreeModel = new OrderElementModel(this.order);
this.orderElementTreeModel = new OrderElementTreeModel(this.order);
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
@ -61,7 +65,7 @@ public class OrderModel implements IOrderModel {
@Override
public void prepareForCreate() {
this.order = new Order();
this.orderElementTreeModel = new OrderElementModel(this.order);
this.orderElementTreeModel = new OrderElementTreeModel(this.order);
this.order.setInitDate(new Date());
}
@ -95,8 +99,14 @@ public class OrderModel implements IOrderModel {
}
@Override
public OrderElementModel getOrderElementTreeModel() {
public OrderElementTreeModel getOrderElementTreeModel() {
return orderElementTreeModel;
}
@Override
public IOrderElementModel getOrderElementModel(OrderElement orderElement) {
orderElementModel.setCurrent(orderElement);
return orderElementModel;
}
}

View file

@ -35,7 +35,7 @@
<hbox>
<label value="Total hours" />
<textbox id="totalHours"
<intbox id="totalHours"
value="@{orderElementController.orderElement.workHours}" />
</hbox>
@ -54,9 +54,47 @@
onClick="orderElementController.addHoursGroup();" />
<button id="deleteHoursGroup" label="Delete hours group"
onClick="orderElementController.deleteHoursGroups();" />
<button id="manageCriterions" label="Manage criterions" />
<button id="manageCriterions" label="Manage criterions"
onClick="orderElementController.manageCriterions();" />
</hbox>
<vbox id="selectCriterions" visible="false">
<label value="Manage criterions" />
<hbox>
<listbox id="selectedCriterionTypes" model="@{orderElementController.selectedCriterionTypes}"
multiple="true" checkmark="true">
<listhead>
<listheader label="Selected types"></listheader>
</listhead>
<listitem self="@{each='criterion'}" value="@{criterion}">
<listcell label="@{criterion.name}"></listcell>
</listitem>
</listbox>
<vbox>
<button id="assignButton" label="&lt;&lt;"
onClick="orderElementController.assignCriterions(criterionTypes.selectedItems);" />
<button id="unassignButton" label="&gt;&gt;"
onClick="orderElementController.unassignCriterions(selectedCriterionTypes.selectedItems);" />
</vbox>
<listbox id="criterionTypes" model="@{orderElementController.criterionTypes}"
multiple="true" checkmark="true">
<listhead>
<listheader label="Criterion types"></listheader>
</listhead>
<listitem self="@{each='criterion'}" value="@{criterion}">
<listcell label="@{criterion.name}"></listcell>
</listitem>
</listbox>
</hbox>
</vbox>
<textbox id="description" rows="4"
value="@{orderElementController.orderElement.description,
save-when='saveButton.onClick'}" />

View file

@ -8,7 +8,7 @@
<?component name="list" inline="true" macroURI="_list.zul"?>
<?component name="edition" inline="true" macroURI="_edition.zul"?>
<?component name="orderElementPopup" inline="true" macroURI="_editOrderElement.zul"?>
<zk >
<zk>
<window self="@{define(content)}"
apply="org.navalplanner.web.orders.OrderCRUDController">
<vbox id="messagesContainer"></vbox>