From ba2ce0371dab26eb22a3bb06dbb1dfecf7e2be0e Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Mon, 29 Oct 2012 17:44:23 +0100 Subject: [PATCH 01/35] Add new columns in SumChargedEffort for first and last timesheet date The first and last date of the task depending on the timesheets will be stored in these two new fields in SumChargedEffort. FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../orders/entities/SumChargedEffort.java | 24 +++++++++++++++++++ .../src/main/resources/db.changelog-1.3.xml | 14 +++++++++++ .../business/orders/entities/Orders.hbm.xml | 6 +++++ 3 files changed, 44 insertions(+) diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java index 46a8839e9..fdd513247 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java @@ -21,6 +21,8 @@ package org.libreplan.business.orders.entities; +import java.util.Date; + import org.libreplan.business.common.BaseEntity; import org.libreplan.business.workingday.EffortDuration; @@ -39,6 +41,10 @@ public class SumChargedEffort extends BaseEntity { private EffortDuration indirectChargedEffort = EffortDuration.zero(); + private Date firstTimesheetDate; + + private Date lastTimesheetDate; + protected SumChargedEffort() {} private SumChargedEffort(OrderElement orderElement) { @@ -93,6 +99,24 @@ public class SumChargedEffort extends BaseEntity { public void reset() { directChargedEffort = EffortDuration.zero(); indirectChargedEffort = EffortDuration.zero(); + firstTimesheetDate = null; + lastTimesheetDate = null; + } + + public Date getFirstTimesheetDate() { + return firstTimesheetDate; + } + + public void setFirstTimesheetDate(Date firstTimesheetDate) { + this.firstTimesheetDate = firstTimesheetDate; + } + + public Date getLastTimesheetDate() { + return lastTimesheetDate; + } + + public void setLastTimesheetDate(Date lastTimesheetDate) { + this.lastTimesheetDate = lastTimesheetDate; } } diff --git a/libreplan-business/src/main/resources/db.changelog-1.3.xml b/libreplan-business/src/main/resources/db.changelog-1.3.xml index b4ce27ed8..3bd067ddd 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.3.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.3.xml @@ -73,4 +73,18 @@ + + + Add columns first_timesheet_date and last_timesheet_date to + sum_charged_effort table + + + + + + + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml index c32dada6c..fc378c102 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml @@ -271,6 +271,12 @@ + + + + From 460896ee8bdea12d879a75e7c250e9eae4b47884 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Tue, 30 Oct 2012 11:16:30 +0100 Subject: [PATCH 02/35] Calculate first/last timesheet dates when recalculating a SumChargedEffortDAO FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../orders/daos/SumChargedEffortDAO.java | 49 +++++++++++++++---- .../orders/entities/SumChargedEffort.java | 6 +++ .../workreports/daos/IWorkReportLineDAO.java | 4 ++ .../workreports/daos/WorkReportLineDAO.java | 22 +++++++++ 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java index babaab2cb..aad876f66 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java @@ -19,7 +19,11 @@ package org.libreplan.business.orders.daos; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -149,9 +153,6 @@ public class SumChargedEffortDAO extends private void addDirectChargedEffort(OrderElement orderElement, EffortDuration effort) { SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); - if (sumChargedEffort == null) { - sumChargedEffort = SumChargedEffort.create(orderElement); - } sumChargedEffort.addDirectChargedEffort(effort); save(sumChargedEffort); @@ -163,9 +164,6 @@ public class SumChargedEffortDAO extends EffortDuration effort) { if (orderElement != null) { SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); - if (sumChargedEffort == null) { - sumChargedEffort = SumChargedEffort.create(orderElement); - } sumChargedEffort.addIndirectChargedEffort(effort); save(sumChargedEffort); @@ -231,6 +229,9 @@ public class SumChargedEffortDAO extends .get(orderElement); if (sumChargedEffort == null) { sumChargedEffort = findByOrderElement(orderElement); + if (sumChargedEffort == null) { + sumChargedEffort = SumChargedEffort.create(orderElement); + } mapSumChargedEfforts.put(orderElement, sumChargedEffort); } return sumChargedEffort; @@ -251,6 +252,7 @@ public class SumChargedEffortDAO extends resetMapSumChargedEfforts(); resetSumChargedEffort(order); calculateDirectChargedEffort(order); + calculateFirstAndLastTimesheetDates(order); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } @@ -258,9 +260,6 @@ public class SumChargedEffortDAO extends private void resetSumChargedEffort(OrderElement orderElement) { SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); - if (sumChargedEffort == null) { - sumChargedEffort = SumChargedEffort.create(orderElement); - } sumChargedEffort.reset(); for (OrderElement each : orderElement.getChildren()) { @@ -281,4 +280,36 @@ public class SumChargedEffortDAO extends addDirectChargedEffort(orderElement, effort); } + private Pair calculateFirstAndLastTimesheetDates( + OrderElement orderElement) { + Pair minMax = workReportLineDAO + .findMinAndMaxDatesByOrderElement(orderElement); + + Set minDates = new HashSet(); + Set maxDates = new HashSet(); + + addIfNotNull(minDates, minMax.getFirst()); + addIfNotNull(maxDates, minMax.getSecond()); + + for (OrderElement child : orderElement.getChildren()) { + Pair minMaxChild = calculateFirstAndLastTimesheetDates(child); + addIfNotNull(minDates, minMaxChild.getFirst()); + addIfNotNull(maxDates, minMaxChild.getSecond()); + } + + SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); + sumChargedEffort.setTimesheetDates(minMax.getFirst(), + minMax.getSecond()); + + return Pair.create( + minDates.isEmpty() ? null : Collections.min(minDates), + maxDates.isEmpty() ? null : Collections.max(maxDates)); + } + + private void addIfNotNull(Collection list, Date date) { + if (date != null) { + list.add(date); + } + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java index fdd513247..b49ccb296 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java @@ -119,4 +119,10 @@ public class SumChargedEffort extends BaseEntity { this.lastTimesheetDate = lastTimesheetDate; } + public void setTimesheetDates(Date firstTimesheetDate, + Date lastTimesheetDate) { + setFirstTimesheetDate(firstTimesheetDate); + setLastTimesheetDate(lastTimesheetDate); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java index 61b410c95..5af312036 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java @@ -28,6 +28,7 @@ import org.libreplan.business.common.daos.IIntegrationEntityDAO; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.reports.dtos.WorkReportLineDTO; import org.libreplan.business.resources.entities.Resource; +import org.libreplan.business.util.Pair; import org.libreplan.business.workreports.entities.WorkReport; import org.libreplan.business.workreports.entities.WorkReportLine; @@ -65,4 +66,7 @@ public interface IWorkReportLineDAO extends List findByResourceFilteredByDateNotInWorkReport( Resource resource, Date start, Date end, WorkReport workReport); + Pair findMinAndMaxDatesByOrderElement( + OrderElement orderElement); + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java index 4a40878d5..27036e0fc 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java @@ -34,6 +34,7 @@ import org.libreplan.business.common.daos.IntegrationEntityDAO; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.reports.dtos.WorkReportLineDTO; import org.libreplan.business.resources.entities.Resource; +import org.libreplan.business.util.Pair; import org.libreplan.business.workreports.entities.WorkReport; import org.libreplan.business.workreports.entities.WorkReportLine; import org.springframework.beans.factory.config.BeanDefinition; @@ -146,4 +147,25 @@ public class WorkReportLineDAO extends IntegrationEntityDAO return criteria.list(); } + @Override + public Pair findMinAndMaxDatesByOrderElement( + OrderElement orderElement) { + + String strQuery = "SELECT MIN(date) AS min, MAX(date) AS max " + + "FROM WorkReportLine " + "WHERE orderElement = :orderElement"; + + Query query = getSession().createQuery(strQuery); + query.setParameter("orderElement", orderElement); + + Object[] result = (Object[]) query.uniqueResult(); + + Date min = null; + Date max = null; + if (result != null) { + min = (Date) result[0]; + max = (Date) result[1]; + } + return Pair.create(min, max); + } + } From 4adc1ec71f1837be27cba69af8710093530bcdfe Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Tue, 30 Oct 2012 11:56:14 +0100 Subject: [PATCH 03/35] Calculate first/last timesheets dates when saving/editing/deleting a timesheet In order to do that, a set of order elements affected by the lines added/edit/removed in a timesheet is calculated before saving the timesheet. Afterwards the first/last dates are recalculated for all the order elements in the set. FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../orders/daos/ISumChargedEffortDAO.java | 22 ++++ .../orders/daos/SumChargedEffortDAO.java | 120 ++++++++++++++++-- .../dashboard/PersonalTimesheetModel.java | 4 + .../web/workreports/WorkReportModel.java | 9 ++ .../ws/common/impl/GenericRESTService.java | 14 +- .../impl/WorkReportServiceREST.java | 25 +++- 6 files changed, 181 insertions(+), 13 deletions(-) diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/ISumChargedEffortDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/ISumChargedEffortDAO.java index 10301a50a..19d0fc17c 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/ISumChargedEffortDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/ISumChargedEffortDAO.java @@ -74,4 +74,26 @@ public interface ISumChargedEffortDAO extends */ void recalculateSumChargedEfforts(Long orderId); + /** + * Returns a {@link Set} of {@link OrderElement OrderElements} affected by + * any change taking into account the lines in the report and the ones to be + * removed. + * + * Usually you call this method to get the set before saving the work + * report. After saving the work report you call + * {@link ISumChargedEffortDAO#recalculateTimesheetDates(Set)} with the + * result of this method. + * + * You can pass null as param if you only have one of the sets. + */ + Set getOrderElementsToRecalculateTimsheetDates( + Set workReportLines, + Set deletedWorkReportLinesSet); + + /** + * Recalulates the first and last timesheets dates for each + * {@link OrderElement} in the {@link Set}. + */ + void recalculateTimesheetDates(Set orderElements); + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java index aad876f66..907ce3bb9 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java @@ -70,6 +70,9 @@ public class SumChargedEffortDAO extends @Autowired private IOrderDAO orderDAO; + @Autowired + private IOrderElementDAO orderElementDAO; + private Map mapSumChargedEfforts; @Override @@ -118,6 +121,7 @@ public class SumChargedEffortDAO extends forceLoadParents(parent); } } + }); previousEffort = previous.getFirst(); @@ -252,7 +256,7 @@ public class SumChargedEffortDAO extends resetMapSumChargedEfforts(); resetSumChargedEffort(order); calculateDirectChargedEffort(order); - calculateFirstAndLastTimesheetDates(order); + calculateTimesheetDates(order); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } @@ -280,7 +284,7 @@ public class SumChargedEffortDAO extends addDirectChargedEffort(orderElement, effort); } - private Pair calculateFirstAndLastTimesheetDates( + private Pair calculateTimesheetDates( OrderElement orderElement) { Pair minMax = workReportLineDAO .findMinAndMaxDatesByOrderElement(orderElement); @@ -292,18 +296,21 @@ public class SumChargedEffortDAO extends addIfNotNull(maxDates, minMax.getSecond()); for (OrderElement child : orderElement.getChildren()) { - Pair minMaxChild = calculateFirstAndLastTimesheetDates(child); + Pair minMaxChild = calculateTimesheetDates(child); addIfNotNull(minDates, minMaxChild.getFirst()); addIfNotNull(maxDates, minMaxChild.getSecond()); } - SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); - sumChargedEffort.setTimesheetDates(minMax.getFirst(), - minMax.getSecond()); - - return Pair.create( + Pair result = Pair.create( minDates.isEmpty() ? null : Collections.min(minDates), maxDates.isEmpty() ? null : Collections.max(maxDates)); + + SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); + sumChargedEffort.setTimesheetDates(result.getFirst(), + result.getSecond()); + save(sumChargedEffort); + + return result; } private void addIfNotNull(Collection list, Date date) { @@ -312,4 +319,101 @@ public class SumChargedEffortDAO extends } } + @Override + @Transactional(readOnly = true) + public Set getOrderElementsToRecalculateTimsheetDates( + Set workReportLines, + Set deletedWorkReportLines) { + Set orderElements = new HashSet(); + + if (workReportLines != null) { + for (final WorkReportLine workReportLine : workReportLines) { + if (!workReportLine.isNewObject()) { + OrderElement previousOrderElement = transactionService + .runOnAnotherTransaction(new IOnTransaction() { + @Override + public OrderElement execute() { + try { + WorkReportLine line = workReportLineDAO + .find(workReportLine.getId()); + + return line.getOrderElement(); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + }); + orderElements.add(previousOrderElement); + } + orderElements.add(workReportLine.getOrderElement()); + } + } + + if (deletedWorkReportLines != null) { + for (WorkReportLine workReportLine : deletedWorkReportLines) { + if (workReportLine.isNewObject()) { + // If the line hasn't been saved, we don't take it into + // account + continue; + } + + // Refresh data from database, because of changes not saved are + // not + // useful for the following operations + sessionFactory.getCurrentSession().refresh(workReportLine); + + orderElements.add(workReportLine.getOrderElement()); + } + } + + return orderElements; + } + + @Override + @Transactional + public void recalculateTimesheetDates(Set orderElements) { + try { + for (OrderElement orderElement : orderElements) { + saveTimesheetDatesRecursively(orderElementDAO.find(orderElement + .getId())); + } + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + + private void saveTimesheetDatesRecursively(OrderElement orderElement) { + if (orderElement != null) { + saveTimesheetDates(orderElement); + saveTimesheetDatesRecursively(orderElement.getParent()); + } + } + + private void saveTimesheetDates(OrderElement orderElement) { + Pair minMax = workReportLineDAO + .findMinAndMaxDatesByOrderElement(orderElement); + + Set minDates = new HashSet(); + Set maxDates = new HashSet(); + + addIfNotNull(minDates, minMax.getFirst()); + addIfNotNull(maxDates, minMax.getSecond()); + + for (OrderElement child : orderElement.getChildren()) { + SumChargedEffort childSumChargedEffort = getByOrderElement(child); + addIfNotNull(minDates, + childSumChargedEffort.getFirstTimesheetDate()); + addIfNotNull(maxDates, childSumChargedEffort.getLastTimesheetDate()); + } + + Pair result = Pair.create(minDates.isEmpty() ? null + : Collections.min(minDates), maxDates.isEmpty() ? null + : Collections.max(maxDates)); + + SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); + sumChargedEffort.setTimesheetDates(result.getFirst(), + result.getSecond()); + save(sumChargedEffort); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/users/dashboard/PersonalTimesheetModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/users/dashboard/PersonalTimesheetModel.java index df067c439..95dd7796f 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/users/dashboard/PersonalTimesheetModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/users/dashboard/PersonalTimesheetModel.java @@ -431,12 +431,16 @@ public class PersonalTimesheetModel implements IPersonalTimesheetModel { // saved as it will not be possible to find it later with // WorkReportDAO.getPersonalTimesheetWorkReport() method. } else { + Set orderElements = sumChargedEffortDAO + .getOrderElementsToRecalculateTimsheetDates( + workReport.getWorkReportLines(), null); sumChargedEffortDAO .updateRelatedSumChargedEffortWithWorkReportLineSet(workReport .getWorkReportLines()); workReport.generateWorkReportLineCodes(entitySequenceDAO .getNumberOfDigitsCode(EntityNameEnum.WORK_REPORT)); workReportDAO.save(workReport); + sumChargedEffortDAO.recalculateTimesheetDates(orderElements); } resetModifiedFields(); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java index f05831956..70de3ad76 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java @@ -274,12 +274,17 @@ public class WorkReportModel extends IntegrationEntityModel implements @Override @Transactional public void confirmSave() throws ValidationException { + Set orderElements = sumChargedEffortDAO + .getOrderElementsToRecalculateTimsheetDates( + workReport.getWorkReportLines(), + deletedWorkReportLinesSet); sumChargedEffortDAO.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(deletedWorkReportLinesSet); sumChargedEffortDAO .updateRelatedSumChargedEffortWithWorkReportLineSet(workReport .getWorkReportLines()); workReportDAO.save(workReport); + sumChargedEffortDAO.recalculateTimesheetDates(orderElements); } @Override @@ -412,10 +417,14 @@ public class WorkReportModel extends IntegrationEntityModel implements //before deleting the report, update OrderElement.SumChargedHours try { workReportDAO.reattach(workReport); + Set orderElements = sumChargedEffortDAO + .getOrderElementsToRecalculateTimsheetDates(null, + workReport.getWorkReportLines()); sumChargedEffortDAO .updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport .getWorkReportLines()); workReportDAO.remove(workReport.getId()); + sumChargedEffortDAO.recalculateTimesheetDates(orderElements); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/common/impl/GenericRESTService.java b/libreplan-webapp/src/main/java/org/libreplan/ws/common/impl/GenericRESTService.java index 278bad001..2e324e383 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/common/impl/GenericRESTService.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/common/impl/GenericRESTService.java @@ -143,6 +143,7 @@ public abstract class GenericRESTService implements IWorkReportService { + private Set orderElements; + @Autowired private IWorkReportDAO workReportDAO; @Autowired private IWorkReportLineDAO workReportLineDAO; - @Autowired - private IOrderElementDAO orderElementDAO; - @Autowired private ISumChargedEffortDAO sumChargedEffortDAO; @@ -120,11 +121,19 @@ public class WorkReportServiceREST extends @Override protected void beforeSaving(WorkReport entity) { + orderElements = sumChargedEffortDAO + .getOrderElementsToRecalculateTimsheetDates( + entity.getWorkReportLines(), null); sumChargedEffortDAO .updateRelatedSumChargedEffortWithWorkReportLineSet(entity .getWorkReportLines()); } + @Override + protected void afterSaving(WorkReport entity) { + sumChargedEffortDAO.recalculateTimesheetDates(orderElements); + } + @Override @GET @Path("/{code}/") @@ -140,10 +149,14 @@ public class WorkReportServiceREST extends public Response removeWorkReport(@PathParam("code") String code) { try { WorkReport workReport = workReportDAO.findByCode(code); + Set orderElements = sumChargedEffortDAO + .getOrderElementsToRecalculateTimsheetDates(null, + workReport.getWorkReportLines()); sumChargedEffortDAO .updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport .getWorkReportLines()); workReportDAO.remove(workReport.getId()); + sumChargedEffortDAO.recalculateTimesheetDates(orderElements); return Response.ok().build(); } catch (InstanceNotFoundException e) { return Response.status(Status.NOT_FOUND).build(); @@ -157,10 +170,14 @@ public class WorkReportServiceREST extends public Response removeWorkReportLine(@PathParam("code") String code) { try { WorkReportLine workReportLine = workReportLineDAO.findByCode(code); + Set orderElements = sumChargedEffortDAO + .getOrderElementsToRecalculateTimsheetDates(null, + Collections.singleton(workReportLine)); sumChargedEffortDAO .updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(new HashSet( Arrays.asList(workReportLine))); workReportLineDAO.remove(workReportLine.getId()); + sumChargedEffortDAO.recalculateTimesheetDates(orderElements); return Response.ok().build(); } catch (InstanceNotFoundException e) { return Response.status(Status.NOT_FOUND).build(); From e4bd4b980f8e3c3aad87c233ed8ffd1c41d23384 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Tue, 30 Oct 2012 10:28:09 +0100 Subject: [PATCH 04/35] Create new default progress type TIMESHEETS The new progress type is read-only, so a new field to mark it in AdvanceType has been added and used in the UI accordingly. FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../bootstrap/PredefinedAdvancedTypes.java | 20 ++++++++++-- .../advance/entities/AdvanceType.java | 10 ++++++ .../src/main/resources/db.changelog-1.3.xml | 16 ++++++++++ .../business/advance/entities/Advance.hbm.xml | 1 + .../IManageOrderElementAdvancesModel.java | 3 ++ .../ManageOrderElementAdvancesController.java | 31 +++++++++++++++---- .../ManageOrderElementAdvancesModel.java | 17 ++++++++-- 7 files changed, 87 insertions(+), 11 deletions(-) diff --git a/libreplan-business/src/main/java/org/libreplan/business/advance/bootstrap/PredefinedAdvancedTypes.java b/libreplan-business/src/main/java/org/libreplan/business/advance/bootstrap/PredefinedAdvancedTypes.java index 26b7eaf3f..c3c45ce5d 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/advance/bootstrap/PredefinedAdvancedTypes.java +++ b/libreplan-business/src/main/java/org/libreplan/business/advance/bootstrap/PredefinedAdvancedTypes.java @@ -36,16 +36,26 @@ public enum PredefinedAdvancedTypes { UNITS("units", new BigDecimal(Integer.MAX_VALUE), new BigDecimal(1), false, false), SUBCONTRACTOR("subcontractor", - new BigDecimal(100), new BigDecimal(0.01), true, false); + new BigDecimal(100), new BigDecimal(0.01), true, false), + TIMESHEETS("timesheets", + new BigDecimal(100), new BigDecimal(0.01), true, false, true); + private PredefinedAdvancedTypes(String name, BigDecimal defaultMaxValue, BigDecimal precision, boolean percentage, boolean qualityForm) { + this(name, defaultMaxValue, precision, percentage, qualityForm, false); + } + + private PredefinedAdvancedTypes(String name, BigDecimal defaultMaxValue, + BigDecimal precision, boolean percentage, boolean qualityForm, + boolean readOnly) { this.name = name; this.defaultMaxValue = defaultMaxValue.setScale(4, BigDecimal.ROUND_HALF_UP); this.unitPrecision = precision.setScale(4, BigDecimal.ROUND_HALF_UP); this.percentage = percentage; this.qualityForm = qualityForm; + this.readOnly = readOnly; } private final String name; @@ -58,9 +68,13 @@ public enum PredefinedAdvancedTypes { private final boolean qualityForm; + private final boolean readOnly; + public AdvanceType createType() { - return AdvanceType.create(name, defaultMaxValue, false, unitPrecision, - true, percentage, qualityForm); + AdvanceType advanceType = AdvanceType.create(name, defaultMaxValue, + false, unitPrecision, true, percentage, qualityForm); + advanceType.setReadOnly(readOnly); + return advanceType; } public String getTypeName() { diff --git a/libreplan-business/src/main/java/org/libreplan/business/advance/entities/AdvanceType.java b/libreplan-business/src/main/java/org/libreplan/business/advance/entities/AdvanceType.java index 74f476ea4..bbbb320c9 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/advance/entities/AdvanceType.java +++ b/libreplan-business/src/main/java/org/libreplan/business/advance/entities/AdvanceType.java @@ -86,6 +86,8 @@ public class AdvanceType extends BaseEntity implements IHumanIdentifiable{ private IAdvanceTypeDAO avanceTypeDAO = Registry.getAdvanceTypeDao(); + private boolean readOnly = false; + /** * Constructor for hibernate. Do not use! */ @@ -271,4 +273,12 @@ public class AdvanceType extends BaseEntity implements IHumanIdentifiable{ return true; } + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + + public boolean isReadOnly() { + return readOnly; + } + } diff --git a/libreplan-business/src/main/resources/db.changelog-1.3.xml b/libreplan-business/src/main/resources/db.changelog-1.3.xml index 3bd067ddd..cd0535082 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.3.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.3.xml @@ -87,4 +87,20 @@ + + + Add new column read_only with default value FALSE to advance_type + table. + + + + + + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/advance/entities/Advance.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/advance/entities/Advance.hbm.xml index 5476067f9..acfe6608e 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/advance/entities/Advance.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/advance/entities/Advance.hbm.xml @@ -23,6 +23,7 @@ + diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/IManageOrderElementAdvancesModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/IManageOrderElementAdvancesModel.java index 4cd1b9b83..80333810e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/IManageOrderElementAdvancesModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/IManageOrderElementAdvancesModel.java @@ -99,6 +99,8 @@ public interface IManageOrderElementAdvancesModel { public boolean isQualityForm(AdvanceAssignment advance); + public boolean isReadOnly(AdvanceAssignment advance); + public boolean lessThanPreviousMeasurements(); public boolean hasConsolidatedAdvances(AdvanceAssignment advance); @@ -134,4 +136,5 @@ public interface IManageOrderElementAdvancesModel { Boolean isAlreadyReportedProgressWith(LocalDate date); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesController.java index 3154f5502..2ff8aa908 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesController.java @@ -366,7 +366,10 @@ public class ManageOrderElementAdvancesController extends if (advance.getAdvanceType() != null) { isQualityForm = manageOrderElementAdvancesModel .isQualityForm(advance); - if (manageOrderElementAdvancesModel + readOnlyAdvance = manageOrderElementAdvancesModel + .isReadOnly(advance); + if (!readOnlyAdvance + && manageOrderElementAdvancesModel .isSubcontratedAdvanceTypeAndSubcontratedTask(advance)) { readOnlyAdvance = true; } @@ -375,12 +378,12 @@ public class ManageOrderElementAdvancesController extends if ((advance instanceof DirectAdvanceAssignment) && ((DirectAdvanceAssignment) advance) .getAdvanceMeasurements().isEmpty() - && !isQualityForm) { + && !isQualityForm && !readOnlyAdvance) { appendComboboxAdvanceType(listItem); } else { appendLabelAdvanceType(listItem); } - appendDecimalBoxMaxValue(listItem, isQualityForm); + appendDecimalBoxMaxValue(listItem, isQualityForm || readOnlyAdvance); appendDecimalBoxValue(listItem); appendLabelPercentage(listItem); appendDateBoxDate(listItem); @@ -401,7 +404,8 @@ public class ManageOrderElementAdvancesController extends for(AdvanceType advanceType : listAdvanceType){ if (!advanceType.getUnitName().equals( PredefinedAdvancedTypes.CHILDREN.getTypeName()) - && !advanceType.isQualityForm()) { + && !advanceType.isQualityForm() + && !advanceType.isReadOnly()) { Comboitem comboItem = new Comboitem(); comboItem.setValue(advanceType); comboItem.setLabel(advanceType.getUnitName()); @@ -461,7 +465,7 @@ public class ManageOrderElementAdvancesController extends } private void appendDecimalBoxMaxValue(final Listitem listItem, - boolean isQualityForm) { + boolean isQualityFormOrReadOnly) { final AdvanceAssignment advanceAssignment = (AdvanceAssignment) listItem .getValue(); final Decimalbox maxValue = new Decimalbox(); @@ -469,7 +473,7 @@ public class ManageOrderElementAdvancesController extends final DirectAdvanceAssignment directAdvanceAssignment; if ((advanceAssignment instanceof IndirectAdvanceAssignment) - || isQualityForm + || isQualityFormOrReadOnly || (advanceAssignment.getAdvanceType() != null && advanceAssignment .getAdvanceType().getPercentage()) || manageOrderElementAdvancesModel @@ -708,6 +712,11 @@ public class ManageOrderElementAdvancesController extends addMeasurementButton.setDisabled(true); addMeasurementButton .setTooltiptext(_("Progress that are reported by quality forms can not be modified")); + } else if ((advance.getAdvanceType() != null) + && (advance.getAdvanceType().isReadOnly())) { + addMeasurementButton.setDisabled(true); + addMeasurementButton + .setTooltiptext(_("This progress type cannot be modified")); } else if (advance instanceof IndirectAdvanceAssignment) { addMeasurementButton.setDisabled(true); addMeasurementButton @@ -739,6 +748,11 @@ public class ManageOrderElementAdvancesController extends removeButton.setDisabled(true); removeButton .setTooltiptext(_("Progress that are reported by quality forms cannot be modified")); + } else if ((advance.getAdvanceType() != null) + && (advance.getAdvanceType().isReadOnly())) { + removeButton.setDisabled(true); + removeButton + .setTooltiptext(_("This progress type cannot be modified")); } else if (advance instanceof IndirectAdvanceAssignment) { removeButton.setDisabled(true); removeButton @@ -1219,6 +1233,11 @@ public class ManageOrderElementAdvancesController extends removeButton.setDisabled(true); removeButton .setTooltiptext(_("Progress measurements that are reported by quality forms cannot be removed")); + } else if ((advance.getAdvanceType() != null) + && (advance.getAdvanceType().isReadOnly())) { + removeButton.setDisabled(true); + removeButton + .setTooltiptext(_("This progress type cannot cannot be removed")); } else if (advance.isFake()) { removeButton.setDisabled(true); removeButton diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesModel.java index 6f177e6ec..553864c29 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesModel.java @@ -161,7 +161,8 @@ public class ManageOrderElementAdvancesModel implements for (AdvanceAssignment advance : listAdvanceAssignmentsCopy) { if ((!listAdvanceAssignments.contains(advance)) && (advance instanceof DirectAdvanceAssignment) - && (!advance.getAdvanceType().isQualityForm())) { + && (!advance.getAdvanceType().isQualityForm()) + && (!advance.getAdvanceType().isReadOnly())) { listAdvanceAssignments.add(advance); } } @@ -337,7 +338,8 @@ public class ManageOrderElementAdvancesModel implements for (AdvanceType advanceType : this.listAdvanceTypes) { if ((advanceType.getUnitName() .equals(PredefinedAdvancedTypes.CHILDREN.getTypeName())) - || (advanceType.isQualityForm())) { + || (advanceType.isQualityForm()) + || advanceType.isReadOnly()) { continue; } if (existsAdvanceTypeAlreadyInThisOrderElement(advanceType)) { @@ -412,6 +414,9 @@ public class ManageOrderElementAdvancesModel implements if (advanceType.isQualityForm()) { return true; } + if (advanceType.isReadOnly()) { + return true; + } } if(isIndirectAdvanceAssignment){ @@ -764,6 +769,14 @@ public class ManageOrderElementAdvancesModel implements return advanceType.isQualityForm(); } + @Override + @Transactional(readOnly = true) + public boolean isReadOnly(AdvanceAssignment advance) { + AdvanceType advanceType = advance.getAdvanceType(); + advanceTypeDAO.reattach(advanceType); + return advanceType.isReadOnly(); + } + @Override @Transactional(readOnly = true) public boolean findIndirectConsolidation( From 69c842350fc0974dda70dd896854eabc59e5c4f1 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Fri, 2 Nov 2012 07:42:37 +0100 Subject: [PATCH 05/35] Add new attribute finished to WorkReportLine FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../workreports/entities/WorkReportLine.java | 11 +++++++++++ .../src/main/resources/db.changelog-1.3.xml | 16 ++++++++++++++++ .../workreports/entities/WorkReports.hbm.xml | 2 ++ 3 files changed, 29 insertions(+) diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java index 15e07a6d6..a33db71ab 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java @@ -78,6 +78,8 @@ public class WorkReportLine extends IntegrationEntity implements Comparable, private TypeOfWorkHours typeOfWorkHours; + private Boolean finished = false; + /** * Constructor for hibernate. Do not use! */ @@ -552,4 +554,13 @@ public class WorkReportLine extends IntegrationEntity implements Comparable, : false; } + @NotNull(message = "finished not specified") + public Boolean isFinished() { + return finished; + } + + public void setFinished(Boolean finished) { + this.finished = finished; + } + } diff --git a/libreplan-business/src/main/resources/db.changelog-1.3.xml b/libreplan-business/src/main/resources/db.changelog-1.3.xml index cd0535082..5c638d5e6 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.3.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.3.xml @@ -103,4 +103,20 @@ columnDataType="BOOLEAN" /> + + + Add new column finished with default value FALSE to + work_report_line table. + + + + + + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/workreports/entities/WorkReports.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/workreports/entities/WorkReports.hbm.xml index 1439f5bac..6ff0c4a85 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/workreports/entities/WorkReports.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/workreports/entities/WorkReports.hbm.xml @@ -162,6 +162,8 @@ + + From a8ba46e505b1fb5512d763f3de1282bdbbe0be36 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Fri, 2 Nov 2012 08:17:25 +0100 Subject: [PATCH 06/35] Add checkbox in work reports standard edition UI FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../java/org/libreplan/web/common/Util.java | 4 +-- .../web/common/components/CapacityPicker.java | 2 +- .../workreports/WorkReportCRUDController.java | 28 +++++++++++++++++++ .../webapp/workreports/css/workreports.css | 6 +++- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java index 30f263d78..3e33a3591 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java @@ -56,6 +56,7 @@ import org.zkoss.zkplus.databind.AnnotateDataBinder; import org.zkoss.zkplus.databind.DataBinder; import org.zkoss.zul.Bandbox; import org.zkoss.zul.Button; +import org.zkoss.zul.Checkbox; import org.zkoss.zul.Combobox; import org.zkoss.zul.Comboitem; import org.zkoss.zul.Datebox; @@ -67,7 +68,6 @@ import org.zkoss.zul.Radio; import org.zkoss.zul.Row; import org.zkoss.zul.Textbox; import org.zkoss.zul.Timebox; -import org.zkoss.zul.api.Checkbox; import org.zkoss.zul.api.Column; /** @@ -446,7 +446,7 @@ public class Util { * The {@link Setter} interface that will implement a set method. * @return The {@link Checkbox} bound */ - public static C bind(final C checkBox, + public static Checkbox bind(final Checkbox checkBox, final Getter getter, final Setter setter) { checkBox.setChecked(getter.get()); checkBox.addEventListener(Events.ON_CHECK, new EventListener() { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/CapacityPicker.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/CapacityPicker.java index 1a710f67c..399b2b5d4 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/components/CapacityPicker.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/components/CapacityPicker.java @@ -24,7 +24,7 @@ import org.libreplan.business.workingday.EffortDuration; import org.libreplan.web.common.Util; import org.libreplan.web.common.Util.Getter; import org.libreplan.web.common.Util.Setter; -import org.zkoss.zul.api.Checkbox; +import org.zkoss.zul.Checkbox; /** * It configures some ZK components to work together and edit a Capacity object diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java index f9311bde7..38144517c 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.LogFactory; import org.hibernate.validator.InvalidValue; @@ -54,6 +55,8 @@ import org.libreplan.web.common.Level; import org.libreplan.web.common.MessagesForUser; import org.libreplan.web.common.OnlyOneVisible; import org.libreplan.web.common.Util; +import org.libreplan.web.common.Util.Getter; +import org.libreplan.web.common.Util.Setter; import org.libreplan.web.common.components.Autocomplete; import org.libreplan.web.common.components.NewDataSortableColumn; import org.libreplan.web.common.components.NewDataSortableGrid; @@ -73,6 +76,7 @@ import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.SelectEvent; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Button; +import org.zkoss.zul.Checkbox; import org.zkoss.zul.Column; import org.zkoss.zul.Columns; import org.zkoss.zul.Comboitem; @@ -815,7 +819,12 @@ public class WorkReportCRUDController extends GenericForwardComposer implements columnHoursType.setLabel(_("Hours type")); columnHoursType.setSclass("hours-type-column"); columns.appendChild(columnHoursType); + NewDataSortableColumn columnFinsihed = new NewDataSortableColumn(); + columnFinsihed.setLabel(_("Done")); + columnFinsihed.setSclass("finished-column"); + columnFinsihed.setTooltiptext(_("Task finished")); NewDataSortableColumn columnCode = new NewDataSortableColumn(); + columns.appendChild(columnFinsihed); columnCode.setLabel(_("Code")); columnCode.setSclass("code-column"); columns.appendChild(columnCode); @@ -1197,6 +1206,24 @@ public class WorkReportCRUDController extends GenericForwardComposer implements row.appendChild(code); } + private void appendFinished(final Row row) { + final WorkReportLine line = (WorkReportLine) row.getValue(); + + Checkbox finished = Util.bind(new Checkbox(), new Getter() { + @Override + public Boolean get() { + return line.isFinished(); + } + }, new Setter() { + @Override + public void set(Boolean value) { + line.setFinished(BooleanUtils.isTrue(value)); + } + }); + + row.appendChild(finished); + } + /** * Append a delete {@link Button} to {@link Row} * @@ -1306,6 +1333,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements appendEffortDuration(row); appendHoursType(row); + appendFinished(row); appendCode(row); appendDeleteButton(row); } diff --git a/libreplan-webapp/src/main/webapp/workreports/css/workreports.css b/libreplan-webapp/src/main/webapp/workreports/css/workreports.css index 4975f5e17..ac8ae9ee5 100644 --- a/libreplan-webapp/src/main/webapp/workreports/css/workreports.css +++ b/libreplan-webapp/src/main/webapp/workreports/css/workreports.css @@ -14,7 +14,7 @@ } .listWorkReportLines .operations-column { - width: 40px; + width: 15px; } .listWorkReportLines .resource-column { @@ -25,6 +25,10 @@ width: 150px; } +.listWorkReportLines .finished-column { + width: 20px; +} + .listWorkReportLines .order-code-column { width: 180px; } From 6c3a915b8d0c6ef4e001657e182ced7cbd4149a5 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Fri, 2 Nov 2012 09:31:32 +0100 Subject: [PATCH 07/35] Implement constraint to check that only one WorkReportLine per task is finished FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../org/libreplan/business/common/Util.java | 40 +++++++++++++++++++ .../workreports/daos/IWorkReportLineDAO.java | 3 ++ .../workreports/daos/WorkReportLineDAO.java | 21 ++++++++++ .../workreports/entities/WorkReport.java | 17 ++++++++ .../workreports/entities/WorkReportLine.java | 19 +++++++++ .../workreports/WorkReportCRUDController.java | 38 +++++++++++++++--- 6 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 libreplan-business/src/main/java/org/libreplan/business/common/Util.java diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/Util.java b/libreplan-business/src/main/java/org/libreplan/business/common/Util.java new file mode 100644 index 000000000..769ad097a --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/common/Util.java @@ -0,0 +1,40 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.common; + +import java.util.Collection; + +/** + * Utilities class.
+ * @author Manuel Rego Casasnovas + */ +public class Util { + + public static boolean contains(Collection collection, + BaseEntity entity) { + for (BaseEntity each : collection) { + if (each.getId().equals(entity.getId())) { + return true; + } + } + return false; + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java index 5af312036..9e22fcab8 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java @@ -69,4 +69,7 @@ public interface IWorkReportLineDAO extends Pair findMinAndMaxDatesByOrderElement( OrderElement orderElement); + List findByOrderElementNotInWorkReportAnotherTransaction( + OrderElement orderElement, WorkReport workReport); + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java index 27036e0fc..ebafb9ee2 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java @@ -40,6 +40,7 @@ import org.libreplan.business.workreports.entities.WorkReportLine; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** @@ -168,4 +169,24 @@ public class WorkReportLineDAO extends IntegrationEntityDAO return Pair.create(min, max); } + @Override + @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) + public List findByOrderElementNotInWorkReportAnotherTransaction( + OrderElement orderElement, WorkReport workReport) { + return findByOrderElementNotInWorkReport(orderElement, workReport); + } + + @SuppressWarnings("unchecked") + private List findByOrderElementNotInWorkReport( + OrderElement orderElement, WorkReport workReport) { + Criteria criteria = getSession().createCriteria(WorkReportLine.class); + + criteria.add(Restrictions.eq("orderElement", orderElement)); + if (!workReport.isNewObject()) { + criteria.add(Restrictions.ne("workReport", workReport)); + } + + return (List) criteria.list(); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReport.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReport.java index b1d501fba..caca48337 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReport.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReport.java @@ -36,6 +36,7 @@ import org.hibernate.validator.Valid; import org.joda.time.LocalDate; import org.libreplan.business.common.IntegrationEntity; import org.libreplan.business.common.Registry; +import org.libreplan.business.common.Util; import org.libreplan.business.common.entities.EntitySequence; import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum; import org.libreplan.business.common.exceptions.InstanceNotFoundException; @@ -538,4 +539,20 @@ public class WorkReport extends IntegrationEntity implements return false; } + @AssertTrue(message = "the same task is marked as finished by more than one timesheet line") + public boolean checkConstraintSameOrderElementFinishedBySeveralWorkReportLines() { + Set finishedOrderElements = new HashSet(); + + for (WorkReportLine line : workReportLines) { + if (line.isFinished()) { + if (Util.contains(finishedOrderElements, line.getOrderElement())) { + return false; + } + finishedOrderElements.add(line.getOrderElement()); + } + } + + return true; + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java index a33db71ab..c291fecc6 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java @@ -23,6 +23,7 @@ package org.libreplan.business.workreports.entities; import java.util.Date; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.apache.commons.lang.StringUtils; @@ -563,4 +564,22 @@ public class WorkReportLine extends IntegrationEntity implements Comparable, this.finished = finished; } + @AssertTrue(message = "there is a timesheet line in another work report marking as finished the same task") + public boolean checkConstraintOrderElementFinishedInAnotherWorkReport() { + if (!finished) { + return true; + } + + List lines = Registry.getWorkReportLineDAO() + .findByOrderElementNotInWorkReportAnotherTransaction( + orderElement, workReport); + for (WorkReportLine line : lines) { + if (line.isFinished()) { + return false; + } + } + + return true; + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java index 38144517c..15ee26fe8 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java @@ -277,7 +277,9 @@ public class WorkReportCRUDController extends GenericForwardComposer implements for (InvalidValue invalidValue : e.getInvalidValues()) { Object value = invalidValue.getBean(); if (value instanceof WorkReport) { - validateWorkReport(); + if (validateWorkReport()) { + messagesForUser.showInvalidValues(e); + } } if (value instanceof WorkReportLine) { WorkReportLine workReportLine = (WorkReportLine) invalidValue.getBean(); @@ -332,6 +334,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements _("cannot be empty")); return false; } + return true; } @@ -456,6 +459,16 @@ public class WorkReportCRUDController extends GenericForwardComposer implements } return false; } + + if (!workReportLine.checkConstraintOrderElementFinishedInAnotherWorkReport()) { + Checkbox checkboxFinished = getFinished(row); + if (checkboxFinished != null) { + String message = _("task is already marked as finished in another timesheet"); + showInvalidMessage(checkboxFinished, message); + } + return false; + } + return true; } @@ -470,7 +483,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements */ private Timebox getTimeboxFinish(Row row) { try { - int position = row.getChildren().size() - 5; + int position = row.getChildren().size() - 6; return (Timebox) row.getChildren().get(position); } catch (Exception e) { return null; @@ -484,7 +497,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements */ private Timebox getTimeboxStart(Row row) { try { - int position = row.getChildren().size() - 6; + int position = row.getChildren().size() - 7; return (Timebox) row.getChildren().get(position); } catch (Exception e) { return null; @@ -498,13 +511,28 @@ public class WorkReportCRUDController extends GenericForwardComposer implements */ private Listbox getTypeOfHours(Row row) { try { - int position = row.getChildren().size() - 3; + int position = row.getChildren().size() - 4; return (Listbox) row.getChildren().get(position); } catch (Exception e) { return null; } } + + /** + * Locates {@link Checkbox} finished in {@link Row} + * @param row + * @return + */ + private Checkbox getFinished(Row row) { + try { + int position = row.getChildren().size() - 3; + return (Checkbox) row.getChildren().get(position); + } catch (Exception e) { + return null; + } + } + /** * Locates {@link Texbox} code in {@link Row} * @param row @@ -527,7 +555,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements */ private Textbox getEffort(Row row) { try { - int position = row.getChildren().size() - 4; + int position = row.getChildren().size() - 5; return (Textbox) row.getChildren().get(position); } catch (Exception e) { return null; From d07686af7efbee606c79cf3039badc60a6e8a0c1 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Fri, 2 Nov 2012 12:36:27 +0100 Subject: [PATCH 08/35] Disable finished checkbox in work reports UI if the task is already finished FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../org/libreplan/business/common/Util.java | 10 +++++++ .../web/workreports/IWorkReportModel.java | 6 +++++ .../workreports/WorkReportCRUDController.java | 5 ++++ .../web/workreports/WorkReportModel.java | 26 +++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/Util.java b/libreplan-business/src/main/java/org/libreplan/business/common/Util.java index 769ad097a..fe8bbacfb 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/common/Util.java +++ b/libreplan-business/src/main/java/org/libreplan/business/common/Util.java @@ -27,6 +27,16 @@ import java.util.Collection; */ public class Util { + public static boolean equals(BaseEntity entity1, BaseEntity entity2) { + if (entity1 == null || entity2 == null) { + return false; + } + if (entity1.getId() == null || entity2.getId() == null) { + return false; + } + return entity1.getId().equals(entity2.getId()); + } + public static boolean contains(Collection collection, BaseEntity entity) { for (BaseEntity each : collection) { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/IWorkReportModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/IWorkReportModel.java index 789507fd7..5c54666ea 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/IWorkReportModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/IWorkReportModel.java @@ -245,4 +245,10 @@ public interface IWorkReportModel extends IIntegrationEntityModel { WorkReportLine getFirstWorkReportLine(); + /** + * Checks if an {@link OrderElement} is finished or not in any + * {@link WorkReportLine} of this report or other report. + */ + boolean isFinished(OrderElement orderElement); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java index 15ee26fe8..381161b04 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java @@ -1249,6 +1249,11 @@ public class WorkReportCRUDController extends GenericForwardComposer implements } }); + if (!line.isFinished() + && workReportModel.isFinished(line.getOrderElement())) { + finished.setDisabled(true); + } + row.appendChild(finished); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java index 70de3ad76..048762e67 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java @@ -34,6 +34,7 @@ import java.util.Set; import org.apache.commons.lang.Validate; import org.hibernate.Hibernate; import org.libreplan.business.common.IntegrationEntity; +import org.libreplan.business.common.Util; import org.libreplan.business.common.daos.IConfigurationDAO; import org.libreplan.business.common.entities.EntityNameEnum; import org.libreplan.business.common.exceptions.InstanceNotFoundException; @@ -51,6 +52,7 @@ import org.libreplan.business.resources.daos.IWorkerDAO; import org.libreplan.business.resources.entities.Resource; import org.libreplan.business.resources.entities.Worker; import org.libreplan.business.workreports.daos.IWorkReportDAO; +import org.libreplan.business.workreports.daos.IWorkReportLineDAO; import org.libreplan.business.workreports.daos.IWorkReportTypeDAO; import org.libreplan.business.workreports.entities.WorkReport; import org.libreplan.business.workreports.entities.WorkReportLabelTypeAssigment; @@ -86,6 +88,9 @@ public class WorkReportModel extends IntegrationEntityModel implements @Autowired private IWorkReportDAO workReportDAO; + @Autowired + private IWorkReportLineDAO workReportLineDAO; + @Autowired private IOrderElementDAO orderElementDAO; @@ -653,4 +658,25 @@ public class WorkReportModel extends IntegrationEntityModel implements return workReport.getWorkReportLines().iterator().next(); } + @Override + @Transactional(readOnly = true) + public boolean isFinished(OrderElement orderElement) { + for (WorkReportLine line : workReport.getWorkReportLines()) { + if (line.isFinished() + && Util.equals(line.getOrderElement(), orderElement)) { + return true; + } + } + + List lines = workReportLineDAO + .findByOrderElementNotInWorkReportAnotherTransaction( + orderElement, workReport); + for (WorkReportLine line : lines) { + if (line.isFinished()) { + return true; + } + } + return false; + } + } From 33d43babf76fc88d64abf25b020350f3f4a81650 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Fri, 2 Nov 2012 12:48:52 +0100 Subject: [PATCH 09/35] Add new attribute finishedTimesheets in SumChargedEffort FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../orders/entities/SumChargedEffort.java | 17 +++++++++++++++++ .../src/main/resources/db.changelog-1.3.xml | 19 +++++++++++++++++++ .../business/orders/entities/Orders.hbm.xml | 2 ++ 3 files changed, 38 insertions(+) diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java index b49ccb296..9dd625c1d 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/SumChargedEffort.java @@ -23,8 +23,10 @@ package org.libreplan.business.orders.entities; import java.util.Date; +import org.apache.commons.lang.BooleanUtils; import org.libreplan.business.common.BaseEntity; import org.libreplan.business.workingday.EffortDuration; +import org.libreplan.business.workreports.entities.WorkReportLine; /** * It represents the efforts charged to an {@link OrderElement}, avoiding the @@ -45,6 +47,13 @@ public class SumChargedEffort extends BaseEntity { private Date lastTimesheetDate; + /** + * Finished according to timesheets. If true it means that + * there's a {@link WorkReportLine} marking as finished this + * {@link OrderElement}. + */ + private Boolean finishedTimesheets = false; + protected SumChargedEffort() {} private SumChargedEffort(OrderElement orderElement) { @@ -125,4 +134,12 @@ public class SumChargedEffort extends BaseEntity { setLastTimesheetDate(lastTimesheetDate); } + public Boolean isFinishedTimesheets() { + return finishedTimesheets; + } + + public void setFinishedTimesheets(Boolean finishedTimesheets) { + this.finishedTimesheets = BooleanUtils.isTrue(finishedTimesheets); + } + } diff --git a/libreplan-business/src/main/resources/db.changelog-1.3.xml b/libreplan-business/src/main/resources/db.changelog-1.3.xml index 5c638d5e6..ca9640cf5 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.3.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.3.xml @@ -119,4 +119,23 @@ columnDataType="BOOLEAN" /> + + + + Add new column finished_timesheets with default value FALSE to + sum_charged_effort table. + + + + + + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml index fc378c102..75854b42e 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml @@ -276,6 +276,8 @@ column="first_timesheet_date" /> +
From 58928b208bbd39ea9d08c35d5a404bf01cb90294 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Fri, 2 Nov 2012 13:20:20 +0100 Subject: [PATCH 10/35] Add implementation to calculate if a task is finished according to timesheets It has been modified the calculation of SumChargedEffort to include the new field finishedTimesheets. FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../orders/daos/ISumChargedEffortDAO.java | 4 +-- .../orders/daos/SumChargedEffortDAO.java | 31 ++++++++++++++++--- .../workreports/daos/IWorkReportLineDAO.java | 2 ++ .../workreports/daos/WorkReportLineDAO.java | 10 ++++++ .../dashboard/PersonalTimesheetModel.java | 2 +- .../web/workreports/WorkReportModel.java | 4 +-- .../impl/WorkReportServiceREST.java | 6 ++-- 7 files changed, 47 insertions(+), 12 deletions(-) diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/ISumChargedEffortDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/ISumChargedEffortDAO.java index 19d0fc17c..c17004896 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/ISumChargedEffortDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/ISumChargedEffortDAO.java @@ -81,7 +81,7 @@ public interface ISumChargedEffortDAO extends * * Usually you call this method to get the set before saving the work * report. After saving the work report you call - * {@link ISumChargedEffortDAO#recalculateTimesheetDates(Set)} with the + * {@link ISumChargedEffortDAO#recalculateTimesheetData(Set)} with the * result of this method. * * You can pass null as param if you only have one of the sets. @@ -94,6 +94,6 @@ public interface ISumChargedEffortDAO extends * Recalulates the first and last timesheets dates for each * {@link OrderElement} in the {@link Set}. */ - void recalculateTimesheetDates(Set orderElements); + void recalculateTimesheetData(Set orderElements); } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java index 907ce3bb9..44bb46163 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/SumChargedEffortDAO.java @@ -256,7 +256,7 @@ public class SumChargedEffortDAO extends resetMapSumChargedEfforts(); resetSumChargedEffort(order); calculateDirectChargedEffort(order); - calculateTimesheetDates(order); + calculateTimesheetData(order); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } @@ -284,7 +284,12 @@ public class SumChargedEffortDAO extends addDirectChargedEffort(orderElement, effort); } - private Pair calculateTimesheetDates( + private void calculateTimesheetData(OrderElement orderElement) { + calculateTimesheetDatesAndChildren(orderElement); + calculateFinishedTimesheetsAndChildren(orderElement); + } + + private Pair calculateTimesheetDatesAndChildren( OrderElement orderElement) { Pair minMax = workReportLineDAO .findMinAndMaxDatesByOrderElement(orderElement); @@ -296,7 +301,7 @@ public class SumChargedEffortDAO extends addIfNotNull(maxDates, minMax.getSecond()); for (OrderElement child : orderElement.getChildren()) { - Pair minMaxChild = calculateTimesheetDates(child); + Pair minMaxChild = calculateTimesheetDatesAndChildren(child); addIfNotNull(minDates, minMaxChild.getFirst()); addIfNotNull(maxDates, minMaxChild.getSecond()); } @@ -319,6 +324,22 @@ public class SumChargedEffortDAO extends } } + private void calculateFinishedTimesheetsAndChildren( + OrderElement orderElement) { + calculateFinishedTimesheets(orderElement); + + for (OrderElement child : orderElement.getChildren()) { + calculateFinishedTimesheetsAndChildren(child); + } + } + + private void calculateFinishedTimesheets(OrderElement orderElement) { + SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); + sumChargedEffort.setFinishedTimesheets(workReportLineDAO + .isFinished(orderElement)); + save(sumChargedEffort); + } + @Override @Transactional(readOnly = true) public Set getOrderElementsToRecalculateTimsheetDates( @@ -371,11 +392,13 @@ public class SumChargedEffortDAO extends @Override @Transactional - public void recalculateTimesheetDates(Set orderElements) { + public void recalculateTimesheetData(Set orderElements) { try { for (OrderElement orderElement : orderElements) { saveTimesheetDatesRecursively(orderElementDAO.find(orderElement .getId())); + calculateFinishedTimesheets(orderElementDAO.find(orderElement + .getId())); } } catch (InstanceNotFoundException e) { throw new RuntimeException(e); diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java index 9e22fcab8..5dda01659 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java @@ -72,4 +72,6 @@ public interface IWorkReportLineDAO extends List findByOrderElementNotInWorkReportAnotherTransaction( OrderElement orderElement, WorkReport workReport); + Boolean isFinished(OrderElement orderElement); + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java index ebafb9ee2..f9a354007 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java @@ -189,4 +189,14 @@ public class WorkReportLineDAO extends IntegrationEntityDAO return (List) criteria.list(); } + @Override + public Boolean isFinished(OrderElement orderElement) { + Criteria criteria = getSession().createCriteria(WorkReportLine.class); + + criteria.add(Restrictions.eq("orderElement", orderElement)); + criteria.add(Restrictions.eq("finished", true)); + + return criteria.uniqueResult() != null; + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/users/dashboard/PersonalTimesheetModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/users/dashboard/PersonalTimesheetModel.java index 95dd7796f..81ed6ef82 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/users/dashboard/PersonalTimesheetModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/users/dashboard/PersonalTimesheetModel.java @@ -440,7 +440,7 @@ public class PersonalTimesheetModel implements IPersonalTimesheetModel { workReport.generateWorkReportLineCodes(entitySequenceDAO .getNumberOfDigitsCode(EntityNameEnum.WORK_REPORT)); workReportDAO.save(workReport); - sumChargedEffortDAO.recalculateTimesheetDates(orderElements); + sumChargedEffortDAO.recalculateTimesheetData(orderElements); } resetModifiedFields(); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java index 048762e67..5cf147a77 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportModel.java @@ -289,7 +289,7 @@ public class WorkReportModel extends IntegrationEntityModel implements .getWorkReportLines()); workReportDAO.save(workReport); - sumChargedEffortDAO.recalculateTimesheetDates(orderElements); + sumChargedEffortDAO.recalculateTimesheetData(orderElements); } @Override @@ -429,7 +429,7 @@ public class WorkReportModel extends IntegrationEntityModel implements .updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport .getWorkReportLines()); workReportDAO.remove(workReport.getId()); - sumChargedEffortDAO.recalculateTimesheetDates(orderElements); + sumChargedEffortDAO.recalculateTimesheetData(orderElements); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/workreports/impl/WorkReportServiceREST.java b/libreplan-webapp/src/main/java/org/libreplan/ws/workreports/impl/WorkReportServiceREST.java index 8a389afc5..1b5043beb 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/workreports/impl/WorkReportServiceREST.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/workreports/impl/WorkReportServiceREST.java @@ -131,7 +131,7 @@ public class WorkReportServiceREST extends @Override protected void afterSaving(WorkReport entity) { - sumChargedEffortDAO.recalculateTimesheetDates(orderElements); + sumChargedEffortDAO.recalculateTimesheetData(orderElements); } @Override @@ -156,7 +156,7 @@ public class WorkReportServiceREST extends .updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport .getWorkReportLines()); workReportDAO.remove(workReport.getId()); - sumChargedEffortDAO.recalculateTimesheetDates(orderElements); + sumChargedEffortDAO.recalculateTimesheetData(orderElements); return Response.ok().build(); } catch (InstanceNotFoundException e) { return Response.status(Status.NOT_FOUND).build(); @@ -177,7 +177,7 @@ public class WorkReportServiceREST extends .updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(new HashSet( Arrays.asList(workReportLine))); workReportLineDAO.remove(workReportLine.getId()); - sumChargedEffortDAO.recalculateTimesheetDates(orderElements); + sumChargedEffortDAO.recalculateTimesheetData(orderElements); return Response.ok().build(); } catch (InstanceNotFoundException e) { return Response.status(Status.NOT_FOUND).build(); From 82bad8a1a72b68d702a4e526afae7e1bc0e5b068 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Mon, 5 Nov 2012 08:03:41 +0100 Subject: [PATCH 11/35] Add new field updatedFromTimesheets to TaskElement FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../planner/entities/TaskElement.java | 11 +++++++++++ .../src/main/resources/db.changelog-1.3.xml | 19 ++++++++++++++++++- .../business/planner/entities/Tasks.hbm.xml | 3 +++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskElement.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskElement.java index 3afb60855..0498ff21f 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskElement.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/TaskElement.java @@ -37,6 +37,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.Validate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -182,6 +183,8 @@ public abstract class TaskElement extends BaseEntity { private Boolean simplifiedAssignedStatusCalculationEnabled = false; + private Boolean updatedFromTimesheets = false; + public void initializeDatesIfNeeded() { if (getIntraDayEndDate() == null || getIntraDayStartDate() == null) { initializeDates(); @@ -833,4 +836,12 @@ public abstract class TaskElement extends BaseEntity { return result; } + public Boolean isUpdatedFromTimesheets() { + return updatedFromTimesheets; + } + + public void setUpdatedFromTimesheets(Boolean updatedFromTimesheets) { + this.updatedFromTimesheets = BooleanUtils.isTrue(updatedFromTimesheets); + } + } diff --git a/libreplan-business/src/main/resources/db.changelog-1.3.xml b/libreplan-business/src/main/resources/db.changelog-1.3.xml index ca9640cf5..2a5a8c764 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.3.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.3.xml @@ -119,7 +119,6 @@ columnDataType="BOOLEAN" /> - @@ -138,4 +137,22 @@ columnDataType="BOOLEAN" /> + + + Add new column updated_from_timesheets with default value FALSE to + task_element table. + + + + + + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/Tasks.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/Tasks.hbm.xml index 43357189d..a370b9024 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/Tasks.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/Tasks.hbm.xml @@ -52,6 +52,9 @@ + + From e9365ed1b092096e6364a18fa976b0efb84d9ec4 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Mon, 5 Nov 2012 09:40:45 +0100 Subject: [PATCH 12/35] Add new button to adapt planning according to timesheets It has been added a new method ICommand.isPlannerCommand() to define if a button should be added in the planner toolbar or in the common toolbar (save and cancel buttons). For the moment, we are using a hard-coded value to know how many buttons we should add in the plannerToolbar. At this moment we have 2 buttons: reassign and adapt planning. FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../main/java/org/zkoss/ganttz/Planner.java | 8 +- .../ganttz/adapters/PlannerConfiguration.java | 5 ++ .../org/zkoss/ganttz/extensions/ICommand.java | 6 ++ .../adaptplanning/AdaptPlanningCommand.java | 69 ++++++++++++++++++ .../adaptplanning/IAdaptPlanningCommand.java | 35 +++++++++ .../web/planner/order/OrderPlanningModel.java | 15 ++++ .../web/planner/order/SaveCommandBuilder.java | 5 ++ .../web/planner/reassign/ReassignCommand.java | 5 ++ .../webapp/common/img/ico_adapt_planning.png | Bin 0 -> 830 bytes 9 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/AdaptPlanningCommand.java create mode 100644 libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/IAdaptPlanningCommand.java create mode 100644 libreplan-webapp/src/main/webapp/common/img/ico_adapt_planning.png diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java index 0e84ef16a..a70fc55d5 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java @@ -509,9 +509,11 @@ public class Planner extends HtmlMacroComponent { } for (CommandContextualized c : contextualizedGlobalCommands) { // Comparison through icon as name is internationalized - if (c.getCommand().getImage() - .equals("/common/img/ico_reassign.png")) { - if (plannerToolbar.getChildren().isEmpty()) { + if (c.getCommand().isPlannerCommand()) { + // FIXME Avoid hard-coding the number of planner commands + // At this moment we have 2 planner commands: reassign and adapt + // planning + if (plannerToolbar.getChildren().size() < 2) { plannerToolbar.appendChild(c.toButton()); } } else { diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java index 0931feeac..864d78cc5 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java @@ -86,6 +86,11 @@ public class PlannerConfiguration implements IDisabilityConfiguration { return false; } + @Override + public boolean isPlannerCommand() { + return false; + } + } private static class NullCommandOnTask implements ICommandOnTask { diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ICommand.java b/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ICommand.java index d883748b3..186ad8412 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ICommand.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ICommand.java @@ -39,4 +39,10 @@ public interface ICommand { boolean isDisabled(); + /** + * Describes if a command is for the planner toolbar. Otherwise it'll be + * inserted in the common toolbar. + */ + boolean isPlannerCommand(); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/AdaptPlanningCommand.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/AdaptPlanningCommand.java new file mode 100644 index 000000000..018035c50 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/AdaptPlanningCommand.java @@ -0,0 +1,69 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.libreplan.web.planner.adaptplanning; + +import static org.libreplan.web.I18nHelper._; + +import org.libreplan.business.planner.entities.TaskElement; +import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.zkoss.ganttz.extensions.IContext; + +/** + * @author Manuel Rego Casasnovas + */ +@Component +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class AdaptPlanningCommand implements IAdaptPlanningCommand { + + private PlanningState planningState; + + @Override + public String getName() { + return _("Adapt planning acording to timesheets"); + } + + @Override + public void doAction(IContext context) { + // TODO Auto-generated method stub + } + + @Override + public String getImage() { + return "/common/img/ico_adapt_planning.png"; + } + + @Override + public boolean isDisabled() { + return false; + } + + @Override + public void setState(PlanningState planningState) { + this.planningState = planningState; + } + + @Override + public boolean isPlannerCommand() { + return true; + } + +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/IAdaptPlanningCommand.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/IAdaptPlanningCommand.java new file mode 100644 index 000000000..3826ad0d8 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/IAdaptPlanningCommand.java @@ -0,0 +1,35 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.libreplan.web.planner.adaptplanning; + +import org.libreplan.business.planner.entities.TaskElement; +import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState; +import org.zkoss.ganttz.extensions.ICommand; + +/** + * Command to adapt planning of a project taking into account information from + * the timesheets. + * + * @author Manuel Rego Casasnovas + */ +public interface IAdaptPlanningCommand extends ICommand { + + public void setState(PlanningState planningState); + +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java index 0830dfb63..8f76bce12 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java @@ -71,6 +71,7 @@ import org.libreplan.business.users.entities.UserRole; import org.libreplan.business.workingday.EffortDuration; import org.libreplan.web.calendars.BaseCalendarModel; import org.libreplan.web.common.ViewSwitcher; +import org.libreplan.web.planner.adaptplanning.IAdaptPlanningCommand; import org.libreplan.web.planner.advances.AdvanceAssignmentPlanningController; import org.libreplan.web.planner.advances.IAdvanceAssignmentPlanningCommand; import org.libreplan.web.planner.allocation.IAdvancedAllocationCommand; @@ -222,6 +223,9 @@ public class OrderPlanningModel implements IOrderPlanningModel { @Autowired private IReassignCommand reassignCommand; + @Autowired + private IAdaptPlanningCommand adaptPlanningCommand; + @Autowired private IResourceAllocationCommand resourceAllocationCommand; @@ -332,6 +336,7 @@ public class OrderPlanningModel implements IOrderPlanningModel { configuration.addGlobalCommand(buildReassigningCommand()); configuration.addGlobalCommand(buildCancelEditionCommand()); + configuration.addGlobalCommand(buildAdaptPlanningCommand()); NullSeparatorCommandOnTask separator = new NullSeparatorCommandOnTask(); @@ -1055,6 +1060,11 @@ public class OrderPlanningModel implements IOrderPlanningModel { return reassignCommand; } + private ICommand buildAdaptPlanningCommand() { + adaptPlanningCommand.setState(planningState); + return adaptPlanningCommand; + } + private ICommand buildCancelEditionCommand() { return new ICommand() { @@ -1097,6 +1107,11 @@ public class OrderPlanningModel implements IOrderPlanningModel { return false; } + @Override + public boolean isPlannerCommand() { + return false; + } + }; } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SaveCommandBuilder.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SaveCommandBuilder.java index 8a065b2ef..438b0455f 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SaveCommandBuilder.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SaveCommandBuilder.java @@ -1038,6 +1038,11 @@ public class SaveCommandBuilder { return disabled; } + @Override + public boolean isPlannerCommand() { + return false; + } + } private static final class LabelCreatorForInvalidValues implements diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/reassign/ReassignCommand.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/reassign/ReassignCommand.java index 7456963f4..5d8671413 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/reassign/ReassignCommand.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/reassign/ReassignCommand.java @@ -375,4 +375,9 @@ public class ReassignCommand implements IReassignCommand { return false; } + @Override + public boolean isPlannerCommand() { + return true; + } + } diff --git a/libreplan-webapp/src/main/webapp/common/img/ico_adapt_planning.png b/libreplan-webapp/src/main/webapp/common/img/ico_adapt_planning.png new file mode 100644 index 0000000000000000000000000000000000000000..6b6aa43c9b3c9a3fe2e1d3612af0b30317732a01 GIT binary patch literal 830 zcmV-E1Ht@>P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyw< z2p1+WyXOP|00O>AL_t(I%dM18XjE4e#((GDJIOd0P4M3|iVM52wuse*2$pVIy0J8y z7Q~7cnpJ6QDM)dlNLCHDBm@P~anWMhN*4u9E7&MlgJ7u?bWsLmicob(Q#HPjm&}`c zkBiBS$>gCC`fc8M=biJt#rg4xs_MBv&T!`HDeg~{Y9jLo34x*~$JEr2qe zOrffDZQaGr?H}TPzWyU$pF4<(VvHf*wgv@gSSYWo-a=51%vg#Dxz;vTF3qzzyM#OU zZ}Y=QfhZoweK&Ff1Ui?lMFB-=U$hJnPz4dN#v%%WK}7KCnVgRJdo*Ijl055IZ^RnQ z@XaAyH1&wi_O+~Rdx6$WmbfxWv+h0or%{ha<(W#^y6$xrXWN*FAK`;%x)NiYakRKr zf(Cl`oaA}*dDmfnzjcNGqETL6vxyy>-@^qB_~04n9-Oz3*88;Z{=AvOg+6?!^4Hy) z(AB$5)AY&7UC-73_0O}q@6>KJ0Ht!V@yAlRIOn}t8QU^%rc^E>A~QG{%l^~d9NAr9 zU4A|NLp^f_({$|omn6QzK=&Y}a*<0neqmL6Cm=YX=G;Hj!)I?FnzK0DJWZu?k^Z3` zUR?DOQ%OuUBp85fCbuAiX4U5f&=62njH>c~$|#BzhYubAE0&CFNkt}21KoqDD%DU0 zz|}67!5RyIS5Myu-#(SeW)}CobBJ#)9tBmLh+vFKU98v525HdmedA-Um42%oC5p!z zNwQET(^#zfD$WPbc=7;iEgiY#4Sb%(Wc^#6#nYFMGg+CyWivSvKh2$ox9P|&Zwx<; z$;2nMywO0ZT%`A>10 Date: Tue, 6 Nov 2012 19:20:41 +0100 Subject: [PATCH 13/35] Implement main operations in adapt planning command * TaskElements are marked or not as updatedFromTimesheets * TaskElement start date is set with a START_IN_FIXED_DATE constraint to the first date in the timesheets * TaskElement end date is set to the last date in the timesheets if this is later than the current end date of the task * Depending on if the task is marked as finishedFromTimesheets, a progress of type TIMESHEETS is added or not. If the task is finished, the end date is set according to last date in the timesheets * TaskElement size and position is updated in the Gantt FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../main/java/org/zkoss/ganttz/Planner.java | 14 ++ .../entities/DirectAdvanceAssignment.java | 5 + .../orders/entities/OrderElement.java | 14 ++ .../adaptplanning/AdaptPlanningCommand.java | 134 +++++++++++++++++- 4 files changed, 166 insertions(+), 1 deletion(-) diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java index a70fc55d5..08c7a1347 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java @@ -944,4 +944,18 @@ public class Planner extends HtmlMacroComponent { } } + public TaskComponent getTaskComponentRelatedTo( + org.zkoss.ganttz.data.Task task) { + TaskList taskList = getTaskList(); + if (taskList != null) { + for (TaskComponent each : taskList.getTaskComponents()) { + if (each.getTask().equals(task)) { + return each; + } + } + } + + return null; + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/advance/entities/DirectAdvanceAssignment.java b/libreplan-business/src/main/java/org/libreplan/business/advance/entities/DirectAdvanceAssignment.java index 4ef909e66..b44c06c30 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/advance/entities/DirectAdvanceAssignment.java +++ b/libreplan-business/src/main/java/org/libreplan/business/advance/entities/DirectAdvanceAssignment.java @@ -263,4 +263,9 @@ public class DirectAdvanceAssignment extends AdvanceAssignment { return !nonCalculatedConsolidations.isEmpty(); } + public void resetAdvanceMeasurements(AdvanceMeasurement advanceMeasurement) { + advanceMeasurements.clear(); + addAdvanceMeasurements(advanceMeasurement); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java index 6c397a3ab..120e11f7a 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java @@ -1584,4 +1584,18 @@ public abstract class OrderElement extends IntegrationEntity implements return false; } + public boolean hasTimesheetsReportingHours() { + if (sumChargedEffort == null) { + return false; + } + return sumChargedEffort.getFirstTimesheetDate() != null; + } + + public boolean isFinishedTimesheets() { + if (sumChargedEffort == null) { + return false; + } + return sumChargedEffort.isFinishedTimesheets(); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/AdaptPlanningCommand.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/AdaptPlanningCommand.java index 018035c50..0e5435862 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/AdaptPlanningCommand.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/adaptplanning/AdaptPlanningCommand.java @@ -20,11 +20,27 @@ package org.libreplan.web.planner.adaptplanning; import static org.libreplan.web.I18nHelper._; +import java.util.Date; +import java.util.List; + +import org.joda.time.LocalDate; +import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes; +import org.libreplan.business.advance.entities.AdvanceMeasurement; +import org.libreplan.business.advance.entities.AdvanceType; +import org.libreplan.business.advance.entities.DirectAdvanceAssignment; +import org.libreplan.business.advance.exceptions.DuplicateAdvanceAssignmentForOrderElementException; +import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException; +import org.libreplan.business.orders.entities.OrderElement; +import org.libreplan.business.planner.entities.PositionConstraintType; +import org.libreplan.business.planner.entities.Task; import org.libreplan.business.planner.entities.TaskElement; +import org.libreplan.business.workingday.IntraDayDate; import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import org.zkoss.ganttz.Planner; +import org.zkoss.ganttz.TaskComponent; import org.zkoss.ganttz.extensions.IContext; /** @@ -43,7 +59,123 @@ public class AdaptPlanningCommand implements IAdaptPlanningCommand { @Override public void doAction(IContext context) { - // TODO Auto-generated method stub + List taskElements = planningState.getRootTask() + .getAllChildren(); + for (TaskElement taskElement : taskElements) { + OrderElement orderElement = taskElement.getOrderElement(); + taskElement.setUpdatedFromTimesheets(orderElement + .hasTimesheetsReportingHours()); + + if (taskElement.isUpdatedFromTimesheets()) { + setStartDateAndConstraint(taskElement, orderElement + .getSumChargedEffort().getFirstTimesheetDate()); + Date lastTimesheetDate = orderElement.getSumChargedEffort() + .getLastTimesheetDate(); + setEndDateIfNeeded(taskElement, lastTimesheetDate); + + if (orderElement.isFinishedTimesheets()) { + setEndDate(taskElement, lastTimesheetDate); + addTimesheetsProgress(orderElement, lastTimesheetDate); + } else { + removeTimesheetsProgressIfAny(orderElement); + } + + updateTask(context, taskElement); + } + } + context.reloadCharts(); + } + + private void setStartDateAndConstraint(TaskElement taskElement, + Date startDate) { + taskElement.setStartDate(startDate); + setStartInFixedDateConstarint(taskElement, startDate); + } + + private void setStartInFixedDateConstarint(TaskElement taskElement, + Date startDate) { + if (taskElement.isTask()) { + Task task = (Task) taskElement; + task.getPositionConstraint() + .update(PositionConstraintType.START_IN_FIXED_DATE, + IntraDayDate.startOfDay(LocalDate + .fromDateFields(startDate))); + } + } + + private void setEndDateIfNeeded(TaskElement taskElement, Date endDate) { + if (taskElement.getEndDate().compareTo(endDate) <= 0) { + setEndDate(taskElement, endDate); + } + } + + private void setEndDate(TaskElement taskElement, Date endDate) { + taskElement.setEndDate(LocalDate.fromDateFields(endDate).plusDays(1) + .toDateTimeAtStartOfDay().toDate()); + } + + private void addTimesheetsProgress(OrderElement orderElement, + Date progressDate) { + AdvanceType timesheetsAdvanceType = getTimesheetsAdvanceType(); + + DirectAdvanceAssignment timesheetsAdvanceAssignment = orderElement + .getDirectAdvanceAssignmentByType(timesheetsAdvanceType); + + if (timesheetsAdvanceAssignment == null) { + timesheetsAdvanceAssignment = DirectAdvanceAssignment.create(false, + timesheetsAdvanceType.getDefaultMaxValue()); + timesheetsAdvanceAssignment.setAdvanceType(timesheetsAdvanceType); + try { + orderElement.addAdvanceAssignment(timesheetsAdvanceAssignment); + } catch (DuplicateValueTrueReportGlobalAdvanceException e) { + // This shouldn't happen as the new advanceAssignment is not + // marked as spread yet + throw new RuntimeException(e); + } catch (DuplicateAdvanceAssignmentForOrderElementException e) { + // If the same type already exists in other element we don't do + // anything + return; + } + } + + DirectAdvanceAssignment spreadAdvanceAssignment = orderElement + .getReportGlobalAdvanceAssignment(); + if (spreadAdvanceAssignment != null) { + spreadAdvanceAssignment.setReportGlobalAdvance(false); + } + + timesheetsAdvanceAssignment.setReportGlobalAdvance(true); + timesheetsAdvanceAssignment.resetAdvanceMeasurements(AdvanceMeasurement + .create(LocalDate.fromDateFields(progressDate), + timesheetsAdvanceType.getDefaultMaxValue())); + } + + private AdvanceType getTimesheetsAdvanceType() { + return PredefinedAdvancedTypes.TIMESHEETS.getType(); + } + + private void removeTimesheetsProgressIfAny(OrderElement orderElement) { + DirectAdvanceAssignment timesheetsAdvanceAssignment = orderElement + .getDirectAdvanceAssignmentByType(getTimesheetsAdvanceType()); + if (timesheetsAdvanceAssignment != null) { + orderElement.removeAdvanceAssignment(timesheetsAdvanceAssignment); + } + } + + private void updateTask(IContext context, + TaskElement taskElement) { + taskElement.updateAdvancePercentageFromOrderElement(); + + Planner planner = (Planner) context.getRelativeTo(); + TaskComponent taskComponent = planner.getTaskComponentRelatedTo(context + .getMapper().findAssociatedBean(taskElement)); + if (taskComponent != null) { + taskComponent.updateTooltipText(); + taskComponent.updateProperties(); + taskComponent.invalidate(); + } + + context.recalculatePosition(taskElement); } @Override From 80601c203420d0a82f25245595cdc94289861f3f Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Wed, 7 Nov 2012 14:57:02 +0100 Subject: [PATCH 14/35] Set properly task position for tasks updated from timesheets even if dependencies have priority The task is marked as fixed if it has been updated from timesheets even in the case that dependencies have priority and it should be moved to a different position. The dependencies will appear as violated. FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../java/org/libreplan/web/planner/TaskElementAdapter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/TaskElementAdapter.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/TaskElementAdapter.java index 08e3e1326..d283bb145 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/TaskElementAdapter.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/TaskElementAdapter.java @@ -1203,7 +1203,8 @@ _( @Override public boolean isFixed() { return taskElement.isLimitingAndHasDayAssignments() - || taskElement.hasConsolidations(); + || taskElement.hasConsolidations() + || taskElement.isUpdatedFromTimesheets(); } @Override From 4397d4313348457c3953a3fb3a2b960e8a83d9f6 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Wed, 7 Nov 2012 14:59:21 +0100 Subject: [PATCH 15/35] Disable constraints combo in tasks updated from timesheets FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../web/planner/taskedition/TaskPropertiesController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java index ad475fb10..ea07b713e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java @@ -138,7 +138,8 @@ public class TaskPropertiesController extends GenericForwardComposer { disabledConstraintsAndAllocations = currentTaskElement .isSubcontractedAndWasAlreadySent() - || currentTaskElement.isLimitingAndHasDayAssignments(); + || currentTaskElement.isLimitingAndHasDayAssignments() + || currentTaskElement.isUpdatedFromTimesheets(); if (!disabledConstraintsAndAllocations && (currentTaskElement.isTask())) { disabledConstraintsAndAllocations = ((Task) currentTaskElement) .isManualAnyAllocation(); From f25897a6a433304d8a22c24fbff5dc6b32c3d489 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Wed, 7 Nov 2012 15:25:10 +0100 Subject: [PATCH 16/35] Disable change of scheduling state point in WBS for tasks updated from timesheets FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../business/orders/entities/OrderElement.java | 8 ++++++++ .../web/orders/OrderElementTreeController.java | 10 ++++++++++ .../web/templates/TemplatesTreeController.java | 6 ++++++ .../java/org/libreplan/web/tree/TreeController.java | 6 +++++- 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java index 120e11f7a..55de35a24 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java @@ -1012,6 +1012,14 @@ public abstract class OrderElement extends IntegrationEntity implements return getCurrentSchedulingData().getTaskSource(); } + public TaskElement getTaskElement() { + TaskSource taskSource = getTaskSource(); + if (taskSource == null) { + return null; + } + return taskSource.getTask(); + } + public Set getTaskElements() { if (getTaskSource() == null) { return Collections.emptySet(); 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 f4d1d7f8e..2a8e41928 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 @@ -41,6 +41,7 @@ import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.orders.entities.OrderLine; import org.libreplan.business.orders.entities.OrderLineGroup; import org.libreplan.business.orders.entities.SchedulingState; +import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.requirements.entities.CriterionRequirement; import org.libreplan.business.templates.entities.OrderElementTemplate; import org.libreplan.business.users.entities.UserRole; @@ -518,6 +519,15 @@ public class OrderElementTreeController extends TreeController { super.removeCodeTextbox(key); } + @Override + protected boolean isUpdatedFromTimesheets(OrderElement currentElement) { + TaskElement taskElement = currentElement.getTaskElement(); + if (taskElement != null) { + return taskElement.isUpdatedFromTimesheets(); + } + return false; + } + } @Override 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 f86df4495..352b5b85a 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 @@ -169,6 +169,12 @@ public class TemplatesTreeController extends return currentElement.getSchedulingState(); } + @Override + protected boolean isUpdatedFromTimesheets( + OrderElementTemplate currentElement) { + return false; + } + } public TemplatesTreeController(IOrderTemplatesModel model, 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 e451989f4..4ccf3dabf 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 @@ -762,7 +762,8 @@ public abstract class TreeController> extends final SchedulingState schedulingState = getSchedulingStateFrom(currentElement); SchedulingStateToggler schedulingStateToggler = new SchedulingStateToggler( schedulingState); - schedulingStateToggler.setReadOnly(readOnly); + schedulingStateToggler.setReadOnly(readOnly + || isUpdatedFromTimesheets(currentElement)); final Treecell cell = addCell( getDecorationFromState(getSchedulingStateFrom(currentElement)), schedulingStateToggler); @@ -1227,6 +1228,9 @@ public abstract class TreeController> extends public void doTry() { } + + protected abstract boolean isUpdatedFromTimesheets(T currentElement); + } public void setColumns(List columns) { From b45c6de1369cdced053d00366c3c6a3354058dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacobo=20Aragunde=20P=C3=A9rez?= Date: Fri, 9 Nov 2012 14:04:09 +0100 Subject: [PATCH 17/35] Bug #1517: Select the parent row in the WBS when it's transformed into a container. FEA: ItEr77S04BugFixing --- .../java/org/libreplan/web/tree/TreeController.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 e451989f4..c4aaa21c7 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 @@ -242,6 +242,10 @@ public abstract class TreeController> extends if (node.isLeaf() && !node.isEmptyLeaf()) { // Then a new container will be created nameTextbox = getRenderer().getNameTextbox(node); + } else { + // select the parent row to add new children ASAP + tree.setSelectedItem(getRenderer().getTreeitemForNode( + newNode.getParent().getThis())); } } else { getModel().addElement(name.getValue(), hours.getValue()); @@ -1072,6 +1076,14 @@ public abstract class TreeController> extends } } + public Treeitem getTreeitemForNode(T node) { + Component cmp = hoursIntBoxByElement.get(node); + while (!(cmp instanceof Treeitem)) { + cmp = cmp.getParent(); + } + return (Treeitem) cmp; + } + private Constraint getHoursConstraintFor(final T line) { return new Constraint() { @Override From 2c98e99d638388f087ee7c55eb78c5f49809532b Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Mon, 12 Nov 2012 13:00:44 +0100 Subject: [PATCH 18/35] Disable resource allocation pop-up for tasks updated from timesheets FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../allocation/AllocationConfiguration.java | 3 ++- .../allocation/AllocationRowsHandler.java | 5 ++++ .../web/planner/allocation/FormBinder.java | 23 ++++++++++++++----- .../ResourceAllocationController.java | 10 +++++--- ..._tabPanelNonLimitingResourceAllocation.zul | 2 +- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/AllocationConfiguration.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/AllocationConfiguration.java index 420554f13..3c13cf7a9 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/AllocationConfiguration.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/AllocationConfiguration.java @@ -107,7 +107,8 @@ public class AllocationConfiguration extends HtmlMacroComponent { .getCalculatedValue())) { radio.setChecked(true); } - radio.setDisabled(formBinder.isAnyManual()); + radio.setDisabled(formBinder.isAnyManual() + || formBinder.isTaskUpdatedFromTimesheets()); } } }; diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/AllocationRowsHandler.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/AllocationRowsHandler.java index a1ce03b4c..ed534013a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/AllocationRowsHandler.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/AllocationRowsHandler.java @@ -423,4 +423,9 @@ public class AllocationRowsHandler { private ArrayList copyOfCurrentRowsToAvoidConcurrentModification() { return new ArrayList(currentRows); } + + public boolean isTaskUpdatedFromTimesheets() { + return task.isUpdatedFromTimesheets(); + } + } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/FormBinder.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/FormBinder.java index 01ce78435..317d7ada6 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/FormBinder.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/FormBinder.java @@ -216,7 +216,7 @@ public class FormBinder { boolean disabled = rows.isEmpty() || (CalculatedValue.NUMBER_OF_HOURS == c) || (c == CalculatedValue.RESOURCES_PER_DAY && !recommendedAllocation) - || isAnyManual(); + || isAnyManual() || isTaskUpdatedFromTimesheets(); this.effortInput.setDisabled(disabled); } @@ -244,13 +244,14 @@ public class FormBinder { allResourcesPerDayVisibilityRule(); applyDisabledRulesOnRows(); this.btnRecommendedAllocation.setDisabled(recommendedAllocation - || isAnyManual()); + || isAnyManual() || isTaskUpdatedFromTimesheets()); } private void applyDisabledRulesOnRows() { for (AllocationRow each : rows) { each.applyDisabledRules(getCalculatedValue(), - recommendedAllocation, isAnyManual()); + recommendedAllocation, isAnyManual() + || isTaskUpdatedFromTimesheets()); } } @@ -358,7 +359,7 @@ public class FormBinder { void applyDisabledRules() { this.taskWorkableDays.setDisabled(allocationRowsHandler .getCalculatedValue() == CalculatedValue.END_DATE - || isAnyManual()); + || isAnyManual() || isTaskUpdatedFromTimesheets()); } private void initializeDateAndDurationFieldsFromTaskOriginalValues() { @@ -461,7 +462,8 @@ public class FormBinder { CalculatedValue c = allocationRowsHandler.getCalculatedValue(); this.allResourcesPerDay.setDisabled(rows.isEmpty() || c == CalculatedValue.RESOURCES_PER_DAY - || !recommendedAllocation || isAnyManual()); + || !recommendedAllocation || isAnyManual() + || isTaskUpdatedFromTimesheets()); this.allResourcesPerDay .setConstraint(constraintForAllResourcesPerDay()); } @@ -518,6 +520,10 @@ public class FormBinder { * exit the edition form */ public boolean accept() { + if (isTaskUpdatedFromTimesheets()) { + return true; + } + Flagged result = resourceAllocationModel .accept(); @@ -703,7 +709,8 @@ public class FormBinder { public void setRecommendedAllocation(Button recommendedAllocation) { this.btnRecommendedAllocation = recommendedAllocation; - this.btnRecommendedAllocation.setDisabled(isAnyManual()); + this.btnRecommendedAllocation.setDisabled(isAnyManual() + || isTaskUpdatedFromTimesheets()); Util.ensureUniqueListener(recommendedAllocation, Events.ON_CLICK, new EventListener() { @Override @@ -935,4 +942,8 @@ public class FormBinder { return false; } + public boolean isTaskUpdatedFromTimesheets() { + return allocationRowsHandler.isTaskUpdatedFromTimesheets(); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/ResourceAllocationController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/ResourceAllocationController.java index e3045f3b6..0b2509bc2 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/ResourceAllocationController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/allocation/ResourceAllocationController.java @@ -602,7 +602,7 @@ public class ResourceAllocationController extends GenericForwardComposer { // On click delete button Button deleteButton = appendDeleteButton(row); - deleteButton.setDisabled(isAnyManual()); + deleteButton.setDisabled(isAnyManualOrTaskUpdatedFromTimesheets()); formBinder.setDeleteButtonFor(data, deleteButton); deleteButton.addEventListener("onClick", new EventListener() { @@ -671,8 +671,12 @@ public class ResourceAllocationController extends GenericForwardComposer { return formBinder != null && formBinder.isAnyNotFlat(); } - public boolean isAnyManual() { - return formBinder != null && formBinder.isAnyManual(); + public boolean isAnyManualOrTaskUpdatedFromTimesheets() { + if (formBinder == null) { + return false; + } + return formBinder.isAnyManual() + || formBinder.isTaskUpdatedFromTimesheets(); } } diff --git a/libreplan-webapp/src/main/webapp/planner/taskpanels/_tabPanelNonLimitingResourceAllocation.zul b/libreplan-webapp/src/main/webapp/planner/taskpanels/_tabPanelNonLimitingResourceAllocation.zul index 302c4ac97..271e6b0ff 100644 --- a/libreplan-webapp/src/main/webapp/planner/taskpanels/_tabPanelNonLimitingResourceAllocation.zul +++ b/libreplan-webapp/src/main/webapp/planner/taskpanels/_tabPanelNonLimitingResourceAllocation.zul @@ -64,7 +64,7 @@