diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderElementTreeController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderElementTreeController.java index 1d140071d..bf1cd578a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderElementTreeController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderElementTreeController.java @@ -23,6 +23,7 @@ package org.libreplan.web.orders; import static org.libreplan.web.I18nHelper._; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -392,6 +393,7 @@ public class OrderElementTreeController extends TreeController { .getOrderElementModel(currentOrderElement); orderElementController.openWindow(model); updateHoursFor(currentOrderElement); + updateBudgetFor(currentOrderElement); } protected void addCodeCell(final OrderElement orderElement) { @@ -617,6 +619,7 @@ public class OrderElementTreeController extends TreeController { public void refreshRow(Treeitem item) { try { getRenderer().updateHoursFor((OrderElement) item.getValue()); + getRenderer().updateBudgetFor((OrderElement) item.getValue()); getRenderer().render(item, item.getValue()); } catch (Exception e) { e.printStackTrace(); @@ -717,4 +720,25 @@ public class OrderElementTreeController extends TreeController { }; } + @Override + protected IBudgetHandler getBudgetHandler() { + return new IBudgetHandler() { + + @Override + public BigDecimal getBudgetFor(OrderElement element) { + return element.getBudget(); + } + + @Override + public void setBudgetHours(OrderElement element, + BigDecimal budget) { + if (element instanceof OrderLine) { + OrderLine line = (OrderLine) element; + line.setBudget(budget); + } + } + + }; + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrdersTreeComponent.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrdersTreeComponent.java index 83784c9dd..f2edaa57a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrdersTreeComponent.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrdersTreeComponent.java @@ -76,6 +76,16 @@ public class OrdersTreeComponent extends TreeComponent { treeRenderer.addHoursCell(currentElement); } + }); + columns.add(new OrdersTreeColumn(_("Budget"), "budget", + _("Total task budget")) { + + @Override + protected void doCell(OrderElementTreeitemRenderer treeRenderer, + OrderElement currentElement) { + treeRenderer.addBudgetCell(currentElement); + } + }); columns.add(new OrdersTreeColumn(_("Must start after"), "estimated_init", diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/templates/TemplatesTreeComponent.java b/libreplan-webapp/src/main/java/org/libreplan/web/templates/TemplatesTreeComponent.java index 32932d574..4ac0e0a55 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/templates/TemplatesTreeComponent.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/templates/TemplatesTreeComponent.java @@ -87,6 +87,15 @@ public class TemplatesTreeComponent extends TreeComponent { renderer.addHoursCell(currentElement); } + }); + result.add(new TemplatesTreeColumn(_("Budget"), "budget") { + + @Override + protected void doCell(TemplatesTreeRenderer renderer, + Treeitem item, OrderElementTemplate currentElement) { + renderer.addBudgetCell(currentElement); + } + }); result.add(new TemplatesTreeColumn( _("Must start after (days since beginning project)"), diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/templates/TemplatesTreeController.java b/libreplan-webapp/src/main/java/org/libreplan/web/templates/TemplatesTreeController.java index 3695cc1a6..657a155c9 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/templates/TemplatesTreeController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/templates/TemplatesTreeController.java @@ -22,6 +22,8 @@ package org.libreplan.web.templates; import static org.libreplan.web.I18nHelper._; +import java.math.BigDecimal; + import org.apache.commons.lang.StringUtils; import org.hibernate.validator.ClassValidator; import org.libreplan.business.orders.entities.SchedulingState; @@ -271,11 +273,33 @@ public class TemplatesTreeController extends }; } + @Override + protected IBudgetHandler getBudgetHandler() { + return new IBudgetHandler() { + + @Override + public BigDecimal getBudgetFor(OrderElementTemplate element) { + return element.getBudget(); + } + + @Override + public void setBudgetHours(OrderElementTemplate element, + BigDecimal budget) { + if (element instanceof OrderLineTemplate) { + OrderLineTemplate line = (OrderLineTemplate) element; + line.setBudget(budget); + } + } + + }; + } + public void refreshRow(Treeitem item) { try { OrderElementTemplate orderElement = (OrderElementTemplate) item .getValue(); getRenderer().updateHoursFor(orderElement); + getRenderer().updateBudgetFor(orderElement); getRenderer().render(item, orderElement); } catch (Exception e) { e.printStackTrace(); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/tree/TreeController.java b/libreplan-webapp/src/main/java/org/libreplan/web/tree/TreeController.java index cdb3535f9..5576521cc 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/tree/TreeController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/tree/TreeController.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -22,6 +22,7 @@ package org.libreplan.web.tree; import static org.libreplan.web.I18nHelper._; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -62,6 +63,7 @@ import org.zkoss.zk.ui.event.KeyEvent; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Button; import org.zkoss.zul.Constraint; +import org.zkoss.zul.Decimalbox; import org.zkoss.zul.Intbox; import org.zkoss.zul.RendererCtrl; import org.zkoss.zul.Textbox; @@ -231,6 +233,7 @@ public abstract class TreeController> extends getModel().addElementAt(node, name.getValue(), hours.getValue()); getRenderer().refreshHoursValueForThisNodeAndParents(node); + getRenderer().refreshBudgetValueForThisNodeAndParents(node); } else { getModel().addElement(name.getValue(), hours.getValue()); } @@ -311,6 +314,7 @@ public abstract class TreeController> extends List parentNodes = getModel().getParents(element); getModel().removeNode(element); getRenderer().refreshHoursValueForNodes(parentNodes); + getRenderer().refreshBudgetValueForNodes(parentNodes); } @Override @@ -562,6 +566,8 @@ public abstract class TreeController> extends private Map hoursIntBoxByElement = new HashMap(); + private Map budgetDecimalboxByElement = new HashMap(); + private KeyboardNavigationHandler navigationHandler = new KeyboardNavigationHandler(); private Treerow currentTreeRow; @@ -747,6 +753,117 @@ public abstract class TreeController> extends protected abstract void addDescriptionCell(final T element); + public void addBudgetCell(final T currentElement) { + Decimalbox decimalboxBudget = buildBudgetDecimalboxFor(currentElement); + budgetDecimalboxByElement.put(currentElement, decimalboxBudget); + if (readOnly) { + decimalboxBudget.setDisabled(true); + } + addCell(decimalboxBudget); + } + + private Decimalbox buildBudgetDecimalboxFor(final T element) { + Decimalbox result = new DecimalboxDirectValue(); + if (isLine(element)) { + Util.bind(result, getBudgetGetterFor(element), + getBudgetSetterFor(element)); + result.setConstraint(getBudgetConstraintFor(element)); + } else { + // If it's a container budget cell is not editable + Util.bind(result, getBudgetGetterFor(element)); + } + return result; + } + + private Getter getBudgetGetterFor(final T element) { + return new Util.Getter() { + @Override + public BigDecimal get() { + return getBudgetHandler().getBudgetFor(element); + } + }; + } + + private Setter getBudgetSetterFor(final T element) { + return new Util.Setter() { + @Override + public void set(BigDecimal value) { + getBudgetHandler().setBudgetHours(element, value); + List parentNodes = getModel().getParents(element); + // Remove the last element because it's an + // Order node, not an OrderElement + parentNodes.remove(parentNodes.size() - 1); + for (T node : parentNodes) { + DecimalboxDirectValue decimalbox = (DecimalboxDirectValue) budgetDecimalboxByElement + .get(node); + BigDecimal budget = getBudgetHandler().getBudgetFor( + node); + if (isInCurrentPage(decimalbox)) { + decimalbox.setValue(budget); + } else { + decimalbox.setValueDirectly(budget); + } + } + } + + private boolean isInCurrentPage(DecimalboxDirectValue intbox) { + Treeitem treeItem = (Treeitem) intbox.getParent() + .getParent().getParent(); + List treeItems = new ArrayList( + tree.getItems()); + int position = treeItems.indexOf(treeItem); + + if (position < 0) { + throw new RuntimeException("Treeitem " + treeItem + + " has to belong to tree.getItems() list"); + } + + return (position / tree.getPageSize()) == tree + .getActivePage(); + } + }; + } + + public void updateBudgetFor(T element) { + if (!readOnly && isLine(element)) { + Decimalbox decimalbox = budgetDecimalboxByElement.get(element); + Treecell tc = (Treecell) decimalbox.getParent(); + decimalbox.invalidate(); + refreshBudgetValueForThisNodeAndParents(element); + } + } + + public void refreshBudgetValueForThisNodeAndParents(T node) { + List nodeAndItsParents = getModel().getParents(node); + nodeAndItsParents.add(node); + refreshBudgetValueForNodes(nodeAndItsParents); + } + + public void refreshBudgetValueForNodes(List nodes) { + for (T node : nodes) { + Decimalbox decimalbox = budgetDecimalboxByElement.get(node); + // For the Order node there is no associated decimalbox + if (decimalbox != null) { + BigDecimal currentBudget = getBudgetHandler().getBudgetFor(node); + decimalbox.setValue(currentBudget); + } + } + } + + private Constraint getBudgetConstraintFor(final T line) { + return new Constraint() { + @Override + public void validate(Component comp, Object value) + throws WrongValueException { + if (((BigDecimal) value).compareTo(BigDecimal.ZERO) < 0) { + throw new WrongValueException(comp, + _("Budget value cannot be negative")); + } + } + + }; + } + public void addHoursCell(final T currentElement) { Intbox intboxHours = buildHoursIntboxFor(currentElement); hoursIntBoxByElement.put(currentElement, intboxHours); @@ -1037,6 +1154,15 @@ public abstract class TreeController> extends protected abstract IHoursGroupHandler getHoursGroupHandler(); + public interface IBudgetHandler { + + BigDecimal getBudgetFor(T element); + + void setBudgetHours(T element, BigDecimal budget); + } + + protected abstract IBudgetHandler getBudgetHandler(); + /** * Disable control buttons (new, up, down, indent, unindent, delete) */ @@ -1117,4 +1243,24 @@ public abstract class TreeController> extends } } + /** + * This class is to give visibility to method + * {@link Decimalbox#setValueDirectly} which is marked as protected in + * {@link Decimalbox} class. + * + *
+ * + * This is needed to prevent calling {@link AbstractComponent#smartUpdate} + * when the {@link Decimalbox} is not in current page. smartUpdate + * is called by {@link Decimalbox#setValue(Integer)}. This call causes a + * JavaScript error when trying to update {@link Decimalbox} that are not in + * current page in the tree. + */ + private class DecimalboxDirectValue extends Decimalbox { + @Override + public void setValueDirectly(Object value) { + super.setValueDirectly(value); + } + } + }