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:
parent
dc77d8b9a3
commit
ccceab9c6f
6 changed files with 1568 additions and 100 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
<label value="Total hours" />
|
||||
<textbox id="totalHours"
|
||||
value="@{orderElementController.orderElement.workHours}" />
|
||||
<div id="fixedHoursCheckbox" />
|
||||
</hbox>
|
||||
|
||||
<listbox id="hoursGroupsListbox" multiple="true"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue