diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/Registry.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/Registry.java index 90589a01b..a7b610bab 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/Registry.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/Registry.java @@ -35,6 +35,7 @@ import org.navalplanner.business.resources.daos.IMachineDAO; import org.navalplanner.business.resources.daos.IWorkerDAO; import org.navalplanner.business.users.daos.IProfileDAO; import org.navalplanner.business.users.daos.IUserDAO; +import org.navalplanner.business.workreports.daos.IWorkReportLineDAO; import org.navalplanner.business.workreports.daos.IWorkReportTypeDAO; import org.springframework.beans.factory.annotation.Autowired; @@ -44,7 +45,7 @@ import org.springframework.beans.factory.annotation.Autowired; * to access DAOs. For the rest of classes (e.g. services, tests, etc.), Spring * DI is a more convenient option. The DAOs or services are added to the * registry as needed. - * + * * @author Óscar González Fernández * @author Fernando Bellas Permuy * @author Javier Moran Rua @@ -103,6 +104,9 @@ public class Registry { @Autowired private IWorkerDAO workerDAO; + + @Autowired + private IWorkReportLineDAO workReportLineDAO; private Registry() { } @@ -174,5 +178,7 @@ public class Registry { public static IWorkerDAO getWorkerDAO() { return getInstance().workerDAO; } - -} \ No newline at end of file + public static IWorkReportLineDAO getWorkReportLineDAO() { + return getInstance().workReportLineDAO; + } +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ITaskElementDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ITaskElementDAO.java index 8a17fbdae..b0f96cd7a 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ITaskElementDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ITaskElementDAO.java @@ -22,10 +22,14 @@ package org.navalplanner.business.planner.daos; import java.util.List; +import org.joda.time.LocalDate; import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.planner.entities.TaskGroup; +import org.navalplanner.business.reports.dtos.CompletedEstimatedHoursPerTaskDTO; +import org.navalplanner.business.reports.dtos.WorkingProgressPerTaskDTO; /** * @author Óscar González Fernández @@ -39,4 +43,10 @@ public interface ITaskElementDAO extends IGenericDAO { List findChildrenOf(TaskGroup each); + List getWorkingProgressPerTaskReport( + Order order, LocalDate deadline); + + List getCompletedEstimatedHoursPerTaskReport( + Order order, LocalDate deadline); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/TaskElementDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/TaskElementDAO.java index f53615ddf..a74969788 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/TaskElementDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/TaskElementDAO.java @@ -23,13 +23,20 @@ package org.navalplanner.business.planner.daos; import java.util.ArrayList; import java.util.List; +import org.hibernate.Query; import org.hibernate.criterion.Restrictions; +import org.joda.time.LocalDate; import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.orders.entities.OrderElement; import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.GenericDayAssignment; import org.navalplanner.business.planner.entities.SpecificDayAssignment; +import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.planner.entities.TaskGroup; +import org.navalplanner.business.reports.dtos.CompletedEstimatedHoursPerTaskDTO; +import org.navalplanner.business.reports.dtos.WorkingProgressPerTaskDTO; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; @@ -80,4 +87,69 @@ public class TaskElementDAO extends GenericDAOHibernate return getSession().createCriteria(TaskElement.class).add( Restrictions.eq("parent", each)).list(); } + + /** + * Returns a list of dtos with calculations for Working progress per task report + * + * @param orders filter by orders + * @param deadline deadline for task + */ + @SuppressWarnings("unchecked") + @Override + @Transactional(readOnly = true) + public List getWorkingProgressPerTaskReport( + Order order, LocalDate deadline) { + + List result = new ArrayList(); + + final List tasks = getTasksByOrderAndDate(order, deadline); + for (Task task: tasks) { + result.add(new WorkingProgressPerTaskDTO(task, deadline)); + } + return result; + } + + private List getTasksByOrderAndDate(Order order, LocalDate deadline) { + + if (deadline == null) { + deadline = new LocalDate(); + } + + final List orders = (order != null) ? order + .getOrderElements() : new ArrayList(); + + String strQuery = + "SELECT task " + + "FROM TaskSource taskSource " + + "LEFT OUTER JOIN taskSource.task task " + + "LEFT OUTER JOIN taskSource.orderElement orderElement " + + "WHERE task IN (SELECT task FROM Task task) " + + "AND task.deadline <= :deadline "; + + if (orders != null && !orders.isEmpty()) { + strQuery += "AND orderElement IN (:orders) "; + } + + // Execute query + Query query = getSession().createQuery(strQuery); + query.setParameter("deadline", deadline); + if (orders != null && !orders.isEmpty()) { + query.setParameterList("orders", orders); + } + + return query.list(); + } + + @Override + public List getCompletedEstimatedHoursPerTaskReport( + Order order, LocalDate deadline) { + + List result = new ArrayList(); + + final List tasks = getTasksByOrderAndDate(order, deadline); + for (Task task: tasks) { + result.add(new CompletedEstimatedHoursPerTaskDTO(task, deadline)); + } + return result; + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/reports/dtos/CompletedEstimatedHoursPerTaskDTO.java b/navalplanner-business/src/main/java/org/navalplanner/business/reports/dtos/CompletedEstimatedHoursPerTaskDTO.java new file mode 100644 index 000000000..634044117 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/reports/dtos/CompletedEstimatedHoursPerTaskDTO.java @@ -0,0 +1,134 @@ +/* + * This file is part of ###PROJECT_NAME### + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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.navalplanner.business.reports.dtos; + +import java.util.List; + +import org.joda.time.LocalDate; +import org.navalplanner.business.common.Registry; +import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.workreports.daos.IWorkReportLineDAO; +import org.navalplanner.business.workreports.entities.WorkReportLine; + +/** + * + * @author Diego Pino Garcia + * + */ +public class CompletedEstimatedHoursPerTaskDTO { + + private IWorkReportLineDAO workReportLineDAO; + + private String taskName; + + private Integer estimatedHours; + + private Integer totalPlannedHours; + + private Integer partialPlannedHours; + + private Integer realHours; + + private CompletedEstimatedHoursPerTaskDTO() { + workReportLineDAO = Registry.getWorkReportLineDAO(); + } + + public CompletedEstimatedHoursPerTaskDTO(Task task, LocalDate date) { + this(); + this.taskName = task.getName(); + this.estimatedHours = task.getHoursSpecifiedAtOrder(); + this.totalPlannedHours = calculatePlannedHours(task, null); + this.partialPlannedHours = calculatePlannedHours(task, date); + this.realHours = calculateRealHours(task, date); + } + + public Integer calculatePlannedHours(Task task, LocalDate date) { + Integer result = new Integer(0); + + final List dayAssignments = task.getDayAssignments(); + if (dayAssignments.isEmpty()) { + return result; + } + + for (DayAssignment dayAssignment : dayAssignments) { + if (date == null || dayAssignment.getDay().compareTo(date) <= 0) { + result += dayAssignment.getHours(); + } + } + return result; + } + + public Integer calculateRealHours(Task task, LocalDate date) { + Integer result = new Integer(0); + + final List workReportLines = workReportLineDAO + .findByOrderElementAndChildren(task.getOrderElement()); + if (workReportLines.isEmpty()) { + return result; + } + + for (WorkReportLine workReportLine : workReportLines) { + final LocalDate workReportLineDate = new LocalDate(workReportLine.getDate()); + if (date == null || workReportLineDate.compareTo(date) <= 0) { + result += workReportLine.getNumHours(); + } + } + return result; + } + + public Integer getEstimatedHours() { + return estimatedHours; + } + + public void setEstimatedHours(Integer estimatedHours) { + this.estimatedHours = estimatedHours; + } + + public Integer getTotalPlannedHours() { + return totalPlannedHours; + } + + public void setTotalPlannedHours(Integer totalPlannedHours) { + this.totalPlannedHours = totalPlannedHours; + } + + public Integer getPartialPlannedHours() { + return partialPlannedHours; + } + + public void setPartialPlannedHours(Integer partialPlannedHours) { + this.partialPlannedHours = partialPlannedHours; + } + + public Integer getRealHours() { + return realHours; + } + + public void setRealHours(Integer realHours) { + this.realHours = realHours; + } + + public String getTaskName() { + return taskName; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/reports/dtos/WorkingProgressPerTaskDTO.java b/navalplanner-business/src/main/java/org/navalplanner/business/reports/dtos/WorkingProgressPerTaskDTO.java new file mode 100644 index 000000000..ef9282824 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/reports/dtos/WorkingProgressPerTaskDTO.java @@ -0,0 +1,234 @@ +/* + * This file is part of ###PROJECT_NAME### + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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.navalplanner.business.reports.dtos; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +import org.joda.time.LocalDate; +import org.navalplanner.business.common.Registry; +import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.workreports.daos.IWorkReportLineDAO; +import org.navalplanner.business.workreports.entities.WorkReportLine; + +/** + * + * @author Diego Pino Garcia + * + */ +public class WorkingProgressPerTaskDTO { + + private IWorkReportLineDAO workReportLineDAO; + + private String taskName; + + private Integer estimatedHours; + + private Integer totalPlannedHours; + + private Integer partialPlannedHours; + + private Integer realHours; + + private BigDecimal averageProgress; + + private Double imputedProgress; + + private Double plannedProgress; + + private BigDecimal costDifference; + + private BigDecimal planningDifference; + + private BigDecimal ratioCostDifference; + + private BigDecimal ratioPlanningDifference; + + private WorkingProgressPerTaskDTO() { + workReportLineDAO = Registry.getWorkReportLineDAO(); + + } + + public WorkingProgressPerTaskDTO(Task task, LocalDate date) { + this(); + this.taskName = task.getName(); + this.estimatedHours = task.getHoursSpecifiedAtOrder(); + this.totalPlannedHours = calculatePlannedHours(task, null); + this.partialPlannedHours = calculatePlannedHours(task, date); + this.realHours = calculateRealHours(task, date); + this.averageProgress = task.getOrderElement().getAdvancePercentage(); + + this.imputedProgress = (totalPlannedHours != 0) ? new Double(realHours / totalPlannedHours.doubleValue()) : new Double(0); + this.plannedProgress = (totalPlannedHours != 0) ? new Double(partialPlannedHours / totalPlannedHours.doubleValue()) : new Double(0); + this.costDifference = calculateCostDifference(averageProgress, + new BigDecimal(totalPlannedHours), new BigDecimal(realHours)); + this.planningDifference = calculatePlanningDifference(averageProgress, + new BigDecimal(totalPlannedHours), new BigDecimal( + partialPlannedHours)); + this.ratioCostDifference = calculateRatioCostDifference(averageProgress, imputedProgress); + this.ratioPlanningDifference = calculateRatioPlanningDifference(averageProgress, plannedProgress); + } + + public Integer calculatePlannedHours(Task task, LocalDate date) { + Integer result = new Integer(0); + + final List dayAssignments = task.getDayAssignments(); + if (dayAssignments.isEmpty()) { + return result; + } + + for (DayAssignment dayAssignment : dayAssignments) { + if (date == null || dayAssignment.getDay().compareTo(date) <= 0) { + result += dayAssignment.getHours(); + } + } + return result; + } + + public Integer calculateRealHours(Task task, LocalDate date) { + Integer result = new Integer(0); + + final List workReportLines = workReportLineDAO + .findByOrderElementAndChildren(task.getOrderElement()); + if (workReportLines.isEmpty()) { + return result; + } + + for (WorkReportLine workReportLine : workReportLines) { + final LocalDate workReportLineDate = new LocalDate(workReportLine.getDate()); + if (date == null || workReportLineDate.compareTo(date) <= 0) { + result += workReportLine.getNumHours(); + } + } + return result; + } + + public Integer getEstimatedHours() { + return estimatedHours; + } + + public void setEstimatedHours(Integer estimatedHours) { + this.estimatedHours = estimatedHours; + } + + public Integer getTotalPlannedHours() { + return totalPlannedHours; + } + + public void setTotalPlannedHours(Integer totalPlannedHours) { + this.totalPlannedHours = totalPlannedHours; + } + + public Integer getPartialPlannedHours() { + return partialPlannedHours; + } + + public void setPartialPlannedHours(Integer partialPlannedHours) { + this.partialPlannedHours = partialPlannedHours; + } + + public Integer getRealHours() { + return realHours; + } + + public void setRealHours(Integer realHours) { + this.realHours = realHours; + } + + public BigDecimal getAverageProgress() { + return averageProgress; + } + + public void setAverageProgress(BigDecimal averageProgress) { + this.averageProgress = averageProgress; + } + + public Double getImputedProgress() { + return imputedProgress; + } + + public void setImputedProgress(Double imputedProgress) { + this.imputedProgress = imputedProgress; + } + + public Double getPlannedProgress() { + return plannedProgress; + } + + public void setPlannedProgress(Double plannedProgress) { + this.plannedProgress = plannedProgress; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public BigDecimal calculateCostDifference(BigDecimal averageProgress, + BigDecimal totalPlannedHours, BigDecimal realHours) { + BigDecimal result = averageProgress; + result = result.multiply(totalPlannedHours); + return result.subtract(realHours); + } + + public BigDecimal calculatePlanningDifference(BigDecimal averageProgress, + BigDecimal totalPlannedHours, BigDecimal partialPlannedHours) { + BigDecimal result = averageProgress; + result = result.multiply(totalPlannedHours); + return result.subtract(partialPlannedHours); + } + + public BigDecimal calculateRatioCostDifference(BigDecimal averageProgress, Double imputedProgress) { + if (imputedProgress.doubleValue() == 0) { + return new BigDecimal(0); + } + return averageProgress.divide(new BigDecimal(imputedProgress), 2, RoundingMode.HALF_UP); + } + + public BigDecimal calculateRatioPlanningDifference(BigDecimal averageProgress, Double plannedProgress) { + if (plannedProgress.doubleValue() == 0) { + return new BigDecimal(0); + } + return averageProgress.divide(new BigDecimal(plannedProgress), 2, RoundingMode.HALF_UP); + } + + public BigDecimal getCostDifference() { + return costDifference; + } + + public BigDecimal getPlanningDifference() { + return planningDifference; + } + + public BigDecimal getRatioCostDifference() { + return ratioCostDifference; + } + + public BigDecimal getRatioPlanningDifference() { + return ratioPlanningDifference; + } + +} diff --git a/navalplanner-webapp/src/main/jasper/completedEstimatedHours.jrxml b/navalplanner-webapp/src/main/jasper/completedEstimatedHours.jrxml new file mode 100644 index 000000000..3767a7143 --- /dev/null +++ b/navalplanner-webapp/src/main/jasper/completedEstimatedHours.jrxml @@ -0,0 +1,192 @@ + + +