Implement money cost calculation in a new class called MoneyCostCalculator

Add a basic test to check that everything is working as expected

FEA: ItEr76S17MoneyCostMonitoringSystem
This commit is contained in:
Manuel Rego Casasnovas 2012-03-19 11:22:14 +01:00
parent 27579c0bfd
commit 213e68c36e
5 changed files with 369 additions and 5 deletions

View file

@ -3,7 +3,7 @@
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
* Copyright (C) 2010-2012 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -21,20 +21,24 @@
package org.libreplan.business.costcategories.daos;
import java.util.Collection;
import java.math.BigDecimal;
import org.hibernate.criterion.Restrictions;
import org.hibernate.Query;
import org.joda.time.LocalDate;
import org.libreplan.business.common.daos.IntegrationEntityDAO;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.costcategories.entities.HourCost;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.resources.entities.Resource;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Jacobo Aragunde Perez <jaragunde@igalia.com>
* @author Diego Pino García <dpino@igalia.com>
* @author Manuel Rego Casasnovas <rego@igalia.com>
*/
@Repository
@Scope(BeanDefinition.SCOPE_SINGLETON)
@ -53,4 +57,25 @@ public class HourCostDAO extends IntegrationEntityDAO<HourCost> implements
super.remove(id);
}
@Override
@Transactional(readOnly = true)
public BigDecimal getPriceCostFromResourceDateAndType(Resource resource,
LocalDate date, TypeOfWorkHours type) {
String strQuery = "SELECT hc.priceCost "
+ "FROM ResourcesCostCategoryAssignment rcca, HourCost hc "
+ "WHERE rcca.costCategory = hc.category "
+ "AND rcca.resource = :resource " + "AND hc.type = :type "
+ "AND rcca.initDate <= :date "
+ "AND (rcca.endDate >= :date OR rcca.endDate IS NULL) "
+ "AND hc.initDate <= :date "
+ "AND (hc.endDate >= :date OR hc.endDate IS NULL)";
Query query = getSession().createQuery(strQuery);
query.setParameter("resource", resource);
query.setParameter("date", date);
query.setParameter("type", type);
return (BigDecimal) query.uniqueResult();
}
}

View file

@ -3,7 +3,7 @@
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
* Copyright (C) 2010-2012 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -21,17 +21,43 @@
package org.libreplan.business.costcategories.daos;
import java.math.BigDecimal;
import org.joda.time.LocalDate;
import org.libreplan.business.common.daos.IIntegrationEntityDAO;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.costcategories.entities.CostCategory;
import org.libreplan.business.costcategories.entities.HourCost;
import org.libreplan.business.costcategories.entities.ResourcesCostCategoryAssignment;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.resources.entities.Resource;
/**
* @author Jacobo Aragunde Perez <jaragunde@igalia.com>
* @author Diego Pino García <dpino@igalia.com>
* @author Manuel Rego Casasnovas <rego@igalia.com>
*/
public interface IHourCostDAO extends IIntegrationEntityDAO<HourCost> {
@Override
public void remove(Long id) throws InstanceNotFoundException;
}
/**
* Returns the price cost of a {@link HourCost} associated with a
* {@link Resource} in a specific {@link LocalDate} and with a concrete
* {@link TypeOfWorkHours}<br />
*
* The association is done through {@link CostCategory} associated to the
* resource in the specific date (from class
* {@link ResourcesCostCategoryAssignment}).
*
* @param resource
* @param date
* @param type
* @return A {@link BigDecimal} with the price cost for a {@link Resource}
* in a {@link LocalDate} for this {@link TypeOfWorkHours}
*/
BigDecimal getPriceCostFromResourceDateAndType(Resource resource,
LocalDate date, TypeOfWorkHours type);
}

View file

@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.libreplan.business.planner.entities;
import java.math.BigDecimal;
import org.libreplan.business.orders.entities.OrderElement;
/**
* Interface to calculate the money cost of a {@link TaskElement}.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public interface IMoneyCostCalculator {
/**
* Returns the money cost of a {@link TaskElement}.<br />
*
* It uses the {@link OrderElement} (or OrderElements) associated to the
* {@link TaskElement} in order to calculate the cost using the following
* formula:<br />
* <tt>Sum of all the hours devoted to a task multiplied by the cost of
* each hour according to these parameters (type of hour, cost category of
* the resource, date of the work report)</tt>
*
* @param The
* {@link TaskElement} to calculate the money cost
* @return Money cost of the task
*/
BigDecimal getMoneyCost(TaskElement taskElement);
}

View file

@ -0,0 +1,75 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.libreplan.business.planner.entities;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import org.libreplan.business.costcategories.daos.IHourCostDAO;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
import org.libreplan.business.workreports.entities.WorkReportLine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* Class to calculate the money cost of a {@link TaskElement}.
*
* Money cost is calculated checking the hours reported for that task and using
* the cost category of each resource in the different dates.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
@Component
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class MoneyCostCalculator implements IMoneyCostCalculator {
@Autowired
private IWorkReportLineDAO workReportLineDAO;
@Autowired
private IHourCostDAO hourCostDAO;
@Override
public BigDecimal getMoneyCost(TaskElement taskElement) {
OrderElement orderElement = taskElement.getOrderElement();
List<WorkReportLine> workReportLines = workReportLineDAO
.findByOrderElementAndChildren(orderElement, false);
BigDecimal result = BigDecimal.ZERO.setScale(2);
for (WorkReportLine workReportLine : workReportLines) {
BigDecimal priceCost = hourCostDAO
.getPriceCostFromResourceDateAndType(
workReportLine.getResource(),
workReportLine.getLocalDate(),
workReportLine.getTypeOfWorkHours());
BigDecimal cost = priceCost.multiply(workReportLine.getEffort()
.toHoursAsDecimalWithScale(2));
result = result.add(cost);
}
return result.setScale(2, RoundingMode.HALF_UP);
}
}

View file

@ -0,0 +1,189 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.libreplan.business.test.planner.entities;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createNiceMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.libreplan.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE;
import static org.libreplan.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE;
import java.math.BigDecimal;
import java.util.Date;
import org.joda.time.LocalDate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.costcategories.daos.ICostCategoryDAO;
import org.libreplan.business.costcategories.daos.ITypeOfWorkHoursDAO;
import org.libreplan.business.costcategories.entities.CostCategory;
import org.libreplan.business.costcategories.entities.HourCost;
import org.libreplan.business.costcategories.entities.ResourcesCostCategoryAssignment;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.orders.daos.IOrderElementDAO;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.orders.entities.OrderLine;
import org.libreplan.business.planner.entities.IMoneyCostCalculator;
import org.libreplan.business.planner.entities.MoneyCostCalculator;
import org.libreplan.business.planner.entities.TaskElement;
import org.libreplan.business.resources.daos.IResourceDAO;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.resources.entities.Worker;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workreports.daos.IWorkReportDAO;
import org.libreplan.business.workreports.daos.IWorkReportTypeDAO;
import org.libreplan.business.workreports.entities.WorkReport;
import org.libreplan.business.workreports.entities.WorkReportLine;
import org.libreplan.business.workreports.entities.WorkReportType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Test for {@link MoneyCostCalculator}
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE,
BUSINESS_SPRING_CONFIG_TEST_FILE })
@Transactional
public class MoneyCostCalculatorTest {
@Autowired
private IMoneyCostCalculator moneyCostCalculator;
@Autowired
private IOrderElementDAO orderElementDAO;
@Autowired
private IResourceDAO resourceDAO;
@Autowired
private IWorkReportDAO workReportDAO;
@Autowired
private IWorkReportTypeDAO workReportTypeDAO;
@Autowired
private ICostCategoryDAO costCategoryDAO;
@Autowired
private ITypeOfWorkHoursDAO typeOfWorkHoursDAO;
private TypeOfWorkHours typeOfWorkHours;
private CostCategory costCategory;
private Resource resource;
private OrderElement orderElement;
private WorkReportType workReportType;
private WorkReport workReport;
private TaskElement taskElement;
private void givenTypeOfWorkHours() {
typeOfWorkHours = TypeOfWorkHours.createUnvalidated(
"default-type-of-work-hours", "default-type-of-work-hours",
true, new BigDecimal(30));
typeOfWorkHoursDAO.save(typeOfWorkHours);
}
private void givenCostCategory() {
costCategory = CostCategory.createUnvalidated("default-cost-category",
"default-cost-category", true);
HourCost hourCost = HourCost.createUnvalidated(
"default-hour-cost", new BigDecimal(50), new LocalDate());
hourCost.setType(typeOfWorkHours);
costCategory.addHourCost(hourCost);
costCategoryDAO.save(costCategory);
}
private void givenResource() {
resource = Worker.createUnvalidated("default-resource",
"default-resource", "default-resource", "default-resource");
ResourcesCostCategoryAssignment resourcesCostCategoryAssignment = ResourcesCostCategoryAssignment
.create();
resourcesCostCategoryAssignment
.setCode("resources-cost-category-assignment");
resourcesCostCategoryAssignment.setCostCategory(costCategory);
resourcesCostCategoryAssignment.setInitDate(new LocalDate());
resource.addResourcesCostCategoryAssignment(resourcesCostCategoryAssignment);
resourceDAO.save(resource);
}
private void givenOrderElement() {
orderElement = OrderLine
.createOrderLineWithUnfixedPercentage(100);
orderElement.setCode("default-order-element");
orderElement.setName("default-order-element");
orderElement.getHoursGroups().get(0).setCode("default-hours-group");
orderElementDAO.save(orderElement);
}
private void giveWorkReportType() {
workReportType = WorkReportType.create("default-work-report-type",
"default-work-report-type");
workReportTypeDAO.save(workReportType);
}
private void givenWorkReport() {
workReport = WorkReport.create(workReportType);
workReport.setCode("default-work-report");
WorkReportLine workReportLine = WorkReportLine.create(workReport);
workReportLine.setCode("default-work-report-line");
workReportLine.setDate(new Date());
workReportLine.setResource(resource);
workReportLine.setOrderElement(orderElement);
workReportLine.setTypeOfWorkHours(typeOfWorkHours);
workReportLine.setEffort(EffortDuration.hours(10));
workReport.addWorkReportLine(workReportLine);
workReportDAO.save(workReport);
}
private void givenTask() {
taskElement = createNiceMock(TaskElement.class);
expect(taskElement.getOrderElement()).andReturn(orderElement)
.anyTimes();
replay(taskElement);
}
private void givenBasicExample() {
givenTypeOfWorkHours();
givenCostCategory();
givenResource();
givenOrderElement();
giveWorkReportType();
givenWorkReport();
givenTask();
}
@Test
public void basicTest() throws InstanceNotFoundException {
givenBasicExample();
assertThat(moneyCostCalculator.getMoneyCost(taskElement),
equalTo(new BigDecimal(500).setScale(2)));
}
}