ItEr14S11CUConfiguracionDeOrganizacionsDeTraballoConUnidadesTraballoItEr13S13: Implemented method setWorkHours of OrderLine class. Added test cases for this method. Removed fixedHours attribute of class OrderLine (not needed anymore). Pending changes in the interface.

This commit is contained in:
Manuel Rego Casasnovas 2009-06-29 11:52:48 +02:00 committed by Javier Moran Rua
parent dc77d8b9a3
commit ccceab9c6f
6 changed files with 1568 additions and 100 deletions

View file

@ -2,7 +2,9 @@ package org.navalplanner.business.orders.entities;
import java.math.BigDecimal;
public class HoursGroup {
import org.hibernate.validator.NotNull;
public class HoursGroup implements Cloneable {
private Long id;
@ -10,7 +12,8 @@ public class HoursGroup {
return id;
}
private Integer workingHours;
@NotNull
private Integer workingHours = 0;
private BigDecimal percentage;

View file

@ -1,31 +1,25 @@
package org.navalplanner.business.orders.entities;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class OrderLine extends OrderElement {
import org.navalplanner.business.orders.entities.HoursGroup.HoursPolicies;
private Boolean fixedHours = false;
public class OrderLine extends OrderElement {
private Set<HoursGroup> hoursGroups = new HashSet<HoursGroup>();
@Override
public Integer getWorkHours() {
int result = 0;
List<HoursGroup> hoursGroups = getHoursGroups();
for (HoursGroup hoursGroup : hoursGroups) {
Integer workingHours = hoursGroup.getWorkingHours();
if (workingHours != null) {
result += workingHours;
}
}
return result;
return calculateTotalHours(hoursGroups);
}
@Override
public List<OrderElement> getChildren() {
// FIXME Shouldn't return null?
return new ArrayList<OrderElement>();
}
@ -45,35 +39,6 @@ public class OrderLine extends OrderElement {
return result;
}
public void setWorkHours(Integer workingHours) {
List<HoursGroup> hoursGroups = getHoursGroups();
// FIXME For the moment we have just one HoursGroup for each OrderLine
if (hoursGroups.isEmpty()) {
HoursGroup hourGroup = new HoursGroup();
hourGroup.setWorkingHours(workingHours);
hoursGroups.add(hourGroup);
} else {
HoursGroup hourGroup = hoursGroups.get(0);
hourGroup.setWorkingHours(workingHours);
}
setHoursGroups(hoursGroups);
}
public void setHoursGroups(List<HoursGroup> hoursGroups) {
this.hoursGroups = new HashSet<HoursGroup>(hoursGroups);
}
public void addHoursGroup(HoursGroup hoursGroup) {
hoursGroups.add(hoursGroup);
}
public void deleteHoursGroup(HoursGroup hoursGroup) {
hoursGroups.remove(hoursGroup);
}
@Override
public List<HoursGroup> getHoursGroups() {
return new ArrayList<HoursGroup>(hoursGroups);
@ -86,12 +51,244 @@ public class OrderLine extends OrderElement {
}
}
public void setFixedHours(Boolean fixedHours) {
this.fixedHours = fixedHours;
public void addHoursGroup(HoursGroup hoursGroup) {
hoursGroups.add(hoursGroup);
recalculatePercentages(hoursGroups);
}
public Boolean isFixedHours() {
return fixedHours;
public void deleteHoursGroup(HoursGroup hoursGroup) {
hoursGroups.remove(hoursGroup);
recalculatePercentages(hoursGroups);
}
/**
* Set the total working hours of the {@link OrderLine} taking into account
* the {@link HoursGroup} policies.
*
* @param workHours
* The desired value to set as total working hours
* @throws IllegalArgumentException
* If parameter is less than 0 or if it's not possible to set
* this value taking into account {@link HoursGroup} policies.
*/
public void setWorkHours(Integer workHours) throws IllegalArgumentException {
if (workHours < 0) {
throw new IllegalArgumentException(
"workHours should be greater or equals to 0");
}
if (hoursGroups.isEmpty()) {
HoursGroup hoursGroup = new HoursGroup();
hoursGroup.setWorkingHours(workHours);
hoursGroup.setPercentage((new BigDecimal(1).setScale(2)));
hoursGroups.add(hoursGroup);
} else {
if (!isTotalHoursValid(workHours)) {
// FIXME change exception type
throw new IllegalArgumentException(
"\"workHours\" value is not valid, taking into "
+ "account the current list of HoursGroup");
}
updateHoursGroups(workHours);
}
}
/**
* Makes the needed modifications in hoursGroups attribute in order to set
* the desired value of working hours.
*
* This method takes into account the different {@link HoursGroup} policies:
*
* {@link HoursGroup} with FIXED_HOURS policy don't change.
*
* If policy is FIXED_PERCENTAGE the new value is calculated for each
* {@link HoursGroup} with this policy. Using round down in order to avoid
* problems.
*
* Hours are proportionally distributed when there're {@link HoursGroup}
* with NO_FIXED policy.
*
* Finally, it creates new {@link HoursGroup} if the're some remaining hours
* (it could happen because of the round down used for operations).
*
* @param workHours
* The value to set as total working hours
*/
private void updateHoursGroups(Integer workHours) {
Set<HoursGroup> newHoursGroups = new HashSet<HoursGroup>();
// Divide HourGroup depending on policy
Set<HoursGroup> fixedHoursGroups = new HashSet<HoursGroup>();
Set<HoursGroup> fixedPercentageGroups = new HashSet<HoursGroup>();
Set<HoursGroup> noFixedGroups = new HashSet<HoursGroup>();
for (HoursGroup hoursGroup : hoursGroups) {
switch (hoursGroup.getHoursPolicy()) {
case FIXED_HOURS:
fixedHoursGroups.add(hoursGroup);
break;
case FIXED_PERCENTAGE:
fixedPercentageGroups.add(hoursGroup);
break;
case NO_FIXED:
default:
noFixedGroups.add(hoursGroup);
break;
}
}
// All the HourGroup with FIXED_HOURS will be kept without changes
newHoursGroups.addAll(fixedHoursGroups);
// For every HourGroup with FIXED_PERCENTAGE, workingHours will be
// calculated
for (HoursGroup hoursGroup : fixedPercentageGroups) {
Integer hours = hoursGroup.getPercentage().multiply(
new BigDecimal(workHours).setScale(2)).toBigInteger()
.intValue();
hoursGroup.setWorkingHours(hours);
newHoursGroups.add(hoursGroup);
}
Integer newTotal = calculateTotalHours(newHoursGroups);
if (newTotal.compareTo(workHours) > 0) {
throw new RuntimeException("Unreachable code");
} else if (newTotal.compareTo(workHours) == 0) {
for (HoursGroup hoursGroup : noFixedGroups) {
hoursGroup.setWorkingHours(0);
newHoursGroups.add(hoursGroup);
}
} else if (newTotal.compareTo(workHours) < 0) {
// Proportional sharing
Integer oldNoFixed = calculateTotalHoursNoFixed(hoursGroups);
Integer newNoFixed = workHours - newTotal;
for (HoursGroup hoursGroup : noFixedGroups) {
Integer newHours = (int) ((float) hoursGroup.getWorkingHours()
/ oldNoFixed * newNoFixed);
hoursGroup.setWorkingHours(newHours);
newHoursGroups.add(hoursGroup);
}
}
// If there're remaining hours
newTotal = calculateTotalHours(newHoursGroups);
if (newTotal.compareTo(workHours) < 0) {
// Add a new HourGroup with the remaining hours
HoursGroup hoursGroup = new HoursGroup();
hoursGroup.setWorkingHours(workHours - newTotal);
newHoursGroups.add(hoursGroup);
}
// Re-calculate percentages
recalculatePercentages(newHoursGroups);
// Set the attribute with the new hours group calculated
hoursGroups = newHoursGroups;
}
/**
* Check if the desired total number of hours is valid taking into account
* {@link HoursGroup} policy restrictions.
*
* @param total
* The desired value
* @return true if the value is valid
*/
private boolean isTotalHoursValid(Integer total) {
Integer newTotal = 0;
for (HoursGroup hoursGroup : hoursGroups) {
switch (hoursGroup.getHoursPolicy()) {
case FIXED_HOURS:
newTotal += hoursGroup.getWorkingHours();
break;
case FIXED_PERCENTAGE:
newTotal += hoursGroup.getPercentage().multiply(
new BigDecimal(total).setScale(2)).toBigInteger()
.intValue();
break;
case NO_FIXED:
default:
break;
}
}
if (newTotal.compareTo(total) > 0) {
return false;
}
return true;
}
/**
* Calculates the total number of working hours in a set of
* {@link HoursGroup}.
*
* @param hoursGroups
* A {@link HoursGroup} set
* @return The sum of working hours
*/
private Integer calculateTotalHours(Set<HoursGroup> hoursGroups) {
Integer result = 0;
for (HoursGroup hoursGroup : hoursGroups) {
result += hoursGroup.getWorkingHours();
}
return result;
}
/**
* Calculates the total number of working hours in a set of
* {@link HoursGroup} taking into account just {@link HoursGroup} with
* NO_FIXED as policy.
*
* @param hoursGroups
* A {@link HoursGroup} set
* @return The sum of NO_FIXED {@link HoursGroup}
*/
private Integer calculateTotalHoursNoFixed(Set<HoursGroup> hoursGroups) {
Integer result = 0;
for (HoursGroup hoursGroup : hoursGroups) {
if (hoursGroup.getHoursPolicy() == HoursPolicies.NO_FIXED) {
result += hoursGroup.getWorkingHours();
}
}
return result;
}
/**
* Re-calculates the percentages in a {@link HoursGroup} set, without modify
* the {@link HoursGroup} with policy FIXED_PERCENTAGE.
*
* @param hoursGroups
* A {@link HoursGroup} set
*/
private void recalculatePercentages(Set<HoursGroup> hoursGroups) {
Integer total = calculateTotalHours(hoursGroups);
BigDecimal totalBigDecimal = new BigDecimal(total).setScale(2);
for (HoursGroup hoursGroup : hoursGroups) {
if (hoursGroup.getHoursPolicy() != HoursPolicies.FIXED_PERCENTAGE) {
if (totalBigDecimal.equals(new BigDecimal(0).setScale(2))) {
hoursGroup.setPercentage(new BigDecimal(0).setScale(2));
} else {
BigDecimal hoursBigDecimal = new BigDecimal(hoursGroup
.getWorkingHours()).setScale(2);
BigDecimal percentage = hoursBigDecimal.divide(
totalBigDecimal, BigDecimal.ROUND_DOWN);
hoursGroup.setPercentage(percentage);
}
}
}
}
}

View file

@ -43,8 +43,6 @@
<joined-subclass name="OrderLine">
<key column="ORDERELEMENTID"></key>
<property name="fixedHours" access="field" />
<set name="hoursGroups" access="field" cascade="all">
<key column="PARENT_ORDER_ELEMENT" not-null="false"></key>
<one-to-many class="HoursGroup" />

View file

@ -1,7 +1,6 @@
package org.navalplanner.web.orders;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@ -17,10 +16,8 @@ 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.Checkbox;
import org.zkoss.zul.Decimalbox;
import org.zkoss.zul.Intbox;
import org.zkoss.zul.Label;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
@ -113,57 +110,11 @@ public class OrderElementController extends GenericForwardComposer {
popup.getFellow("deleteHoursGroup").setVisible(true);
}
fillFixedHoursCheckbox(orderElement);
Util.reloadBindings(popup);
popup.open(popup.getParent(), "start-after");
}
/**
* Private method that just fills the Div with id "fixedHoursCheckbox" in
* the .zul.
*
* If the parameter is a {@link OrderLine} the method adds the needed
* checkbox.
*
* @param orderElement
* {@link OrderElement} that is been rendered
*/
private void fillFixedHoursCheckbox(final OrderElement orderElement) {
// Get the Div with id "fixedHoursCheckbox"
Component fixedHoursCheckbox = popup.getFellow("fixedHoursCheckbox");
// Empty the content of the Div
// Making a copy to avoid a ConcurrentModificationException
List<Component> children = new ArrayList<Component>(fixedHoursCheckbox
.getChildren());
for (Component component : children) {
fixedHoursCheckbox.removeChild(component);
}
// If is a leaf
if (orderElement instanceof OrderLine) {
// Add specific fields
fixedHoursCheckbox.appendChild(Util.bind(new Checkbox(),
new Util.Getter<Boolean>() {
@Override
public Boolean get() {
return ((OrderLine) orderElement).isFixedHours();
}
}, new Util.Setter<Boolean>() {
@Override
public void set(Boolean value) {
((OrderLine) orderElement).setFixedHours(value);
}
}));
fixedHoursCheckbox.appendChild(new Label("Fixed hours"));
}
}
/**
* Just close the {@link Popup}
*/
@ -243,6 +194,9 @@ public class OrderElementController extends GenericForwardComposer {
listitem.setParent(hoursPolicyListBox);
}
Decimalbox decimalBox = new Decimalbox();
decimalBox.setScale(2);
// If is a container
if (orderElement instanceof OrderLineGroup) {
// Just getters are needed
@ -258,7 +212,7 @@ public class OrderElementController extends GenericForwardComposer {
}));
// Percentage
cellPercentage.appendChild(Util.bind(new Decimalbox(),
cellPercentage.appendChild(Util.bind(decimalBox,
new Util.Getter<BigDecimal>() {
@Override
@ -290,7 +244,7 @@ public class OrderElementController extends GenericForwardComposer {
}
});
final Decimalbox percentage = Util.bind(new Decimalbox(),
final Decimalbox percentage = Util.bind(decimalBox,
new Util.Getter<BigDecimal>() {
@Override

View file

@ -37,7 +37,6 @@
<label value="Total hours" />
<textbox id="totalHours"
value="@{orderElementController.orderElement.workHours}" />
<div id="fixedHoursCheckbox" />
</hbox>
<listbox id="hoursGroupsListbox" multiple="true"