/* * 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.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.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.libreplan.business.IDataBootstrap; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.IOnTransaction; 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.expensesheet.daos.IExpenseSheetDAO; import org.libreplan.business.expensesheet.entities.ExpenseSheet; import org.libreplan.business.expensesheet.entities.ExpenseSheetLine; import org.libreplan.business.orders.daos.IOrderElementDAO; import org.libreplan.business.orders.daos.ISumExpensesDAO; 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.planner.entities.IMoneyCostCalculator; import org.libreplan.business.planner.entities.MoneyCostCalculator; 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.scenarios.IScenarioManager; import org.libreplan.business.scenarios.daos.IOrderVersionDAO; import org.libreplan.business.scenarios.entities.OrderVersion; 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 }) public class MoneyCostCalculatorTest { @javax.annotation.Resource private IDataBootstrap scenariosBootstrap; @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; @Autowired private IOrderVersionDAO orderVersionDAO; @Autowired private IScenarioManager scenarioManager; @Autowired private ISumExpensesDAO sumExpensesDAO; @Autowired private IExpenseSheetDAO expenseSheetDAO; @Autowired private IAdHocTransactionService transactionService; private List<TypeOfWorkHours> typesOfWorkHours = new ArrayList<>(); private CostCategory costCategory; private Resource resource; private ExpenseSheet expenseSheet; private List<OrderElement> orderElements = new ArrayList<>(); private WorkReportType workReportType; private WorkReport workReport; @Before public void loadRequiredData() { scenariosBootstrap.loadRequiredData(); } private void givenTypeOfWorkHours(BigDecimal defaultPrice) { TypeOfWorkHours typeOfWorkHours = TypeOfWorkHours.createUnvalidated( "default-type-of-work-hours-" + UUID.randomUUID(), "default-type-of-work-hours-" + UUID.randomUUID(), true, defaultPrice); typeOfWorkHoursDAO.save(typeOfWorkHours); typesOfWorkHours.add(typeOfWorkHours); } private void givenCostCategory() { costCategory = CostCategory.createUnvalidated( "default-cost-category" + UUID.randomUUID(), "default-cost-category" + UUID.randomUUID(), true); HourCost hourCost = HourCost.createUnvalidated( "default-hour-cost" + UUID.randomUUID(), new BigDecimal(50), new LocalDate()); hourCost.setType(typesOfWorkHours.get(0)); costCategory.addHourCost(hourCost); costCategoryDAO.save(costCategory); } private void givenResource(boolean relatedWithCostCategory) { resource = Worker.createUnvalidated( "default-resource" + UUID.randomUUID(), "default-resource", "default-resource", "default-resource"); if (relatedWithCostCategory) { ResourcesCostCategoryAssignment resourcesCostCategoryAssignment = ResourcesCostCategoryAssignment.create(); resourcesCostCategoryAssignment.setCode("resources-cost-category-assignment" + UUID.randomUUID()); resourcesCostCategoryAssignment.setCostCategory(costCategory); resourcesCostCategoryAssignment.setInitDate(new LocalDate()); resource.addResourcesCostCategoryAssignment(resourcesCostCategoryAssignment); } resourceDAO.save(resource); } private void givenOrderElement() { OrderElement orderElement = OrderLine.createOrderLineWithUnfixedPercentage(100); orderElement.setCode("default-order-element-" + UUID.randomUUID()); orderElement.setName("default-order-element-" + UUID.randomUUID()); orderElement.getHoursGroups().get(0).setCode("default-hours-group-" + UUID.randomUUID()); orderElementDAO.save(orderElement); orderElements.add(orderElement); } private void givenOrderLineGroupWithTwoLines() { OrderLineGroup orderLineGroup = OrderLineGroup.create(); orderLineGroup.setCode("default-order-line-group-" + UUID.randomUUID()); orderLineGroup.setName("default-order-line-group-" + UUID.randomUUID()); OrderVersion orderVersion = OrderVersion.createInitialVersion(scenarioManager.getCurrent()); orderVersionDAO.save(orderVersion); orderLineGroup.useSchedulingDataFor(orderVersion); OrderLine orderLine1 = OrderLine.createOrderLineWithUnfixedPercentage(100); orderLine1.setCode("order-line-1-" + UUID.randomUUID()); orderLine1.setName("order-line-1-" + UUID.randomUUID()); orderLine1.getHoursGroups().get(0).setCode("hours-group-1-" + UUID.randomUUID()); orderLineGroup.add(orderLine1); OrderLine orderLine2 = OrderLine.createOrderLineWithUnfixedPercentage(100); orderLine2.setCode("order-line-2-" + UUID.randomUUID()); orderLine2.setName("order-line-2-" + UUID.randomUUID()); orderLine2.getHoursGroups().get(0).setCode("hours-group-2-" + UUID.randomUUID()); orderLineGroup.add(orderLine2); orderElementDAO.save(orderLineGroup); orderElements.add(orderLineGroup); orderElements.add(orderLine1); orderElements.add(orderLine2); } private void giveWorkReportType() { workReportType = WorkReportType.create("default-work-report-type", "default-work-report-type"); workReportTypeDAO.save(workReportType); } private void givenWorkReport() { givenWorkReport(null); } private void givenWorkReport(List<Integer> hoursList) { workReport = WorkReport.create(workReportType); workReport.setCode("default-work-report"); for (OrderElement each : orderElements) { int hours = 10; if (hoursList != null) { hours = hoursList.get(orderElements.indexOf(each)); } workReport.addWorkReportLine(createWorkReportLine(each, hours)); } workReportDAO.save(workReport); } private void givenWorkReportWithSeveralLines(List<Integer> hoursList, List<TypeOfWorkHours> types) { workReport = WorkReport.create(workReportType); workReport.setCode("default-work-report"); for (Integer hour : hoursList) { workReport.addWorkReportLine(createWorkReportLine(hour, types.get(hoursList.indexOf(hour)))); } workReportDAO.save(workReport); } private WorkReportLine createWorkReportLine(OrderElement orderElement, Integer hours) { WorkReportLine workReportLine = WorkReportLine.create(workReport); workReportLine.setCode("default-work-report-line-" + UUID.randomUUID()); workReportLine.setDate(new Date()); workReportLine.setResource(resource); workReportLine.setOrderElement(orderElement); workReportLine.setTypeOfWorkHours(typesOfWorkHours.get(0)); workReportLine.setEffort(EffortDuration.hours(hours)); return workReportLine; } private WorkReportLine createWorkReportLine(Integer hours, TypeOfWorkHours type) { WorkReportLine workReportLine = WorkReportLine.create(workReport); workReportLine.setCode("default-work-report-line-" + UUID.randomUUID()); workReportLine.setDate(new Date()); workReportLine.setResource(resource); workReportLine.setOrderElement(orderElements.get(0)); workReportLine.setTypeOfWorkHours(type); workReportLine.setEffort(EffortDuration.hours(hours)); return workReportLine; } private void givenBasicExample() { givenTypeOfWorkHours(new BigDecimal(30)); givenCostCategory(); givenResource(true); givenOrderElement(); giveWorkReportType(); givenWorkReport(); } private void givenBasicExampleWithoutCostCategoryRelationship() { givenTypeOfWorkHours(new BigDecimal(30)); givenResource(false); givenOrderElement(); giveWorkReportType(); givenWorkReport(); } private void givenExampleOrderLineGroup() { givenTypeOfWorkHours(new BigDecimal(30)); givenCostCategory(); givenResource(true); givenOrderLineGroupWithTwoLines(); giveWorkReportType(); givenWorkReport(); } private void givenExampleOrderLineGroupWithDifferentHours(List<Integer> hoursList) { givenTypeOfWorkHours(new BigDecimal(30)); givenCostCategory(); givenResource(true); givenOrderLineGroupWithTwoLines(); giveWorkReportType(); givenWorkReport(hoursList); } private void givenExampleWithoutCostCategoryRelationshipButDifferentTypeOfHours(List<Integer> hoursList, List<BigDecimal> pricesList) { for (BigDecimal price : pricesList) { givenTypeOfWorkHours(price); } givenResource(false); givenOrderElement(); giveWorkReportType(); givenWorkReportWithSeveralLines(hoursList, typesOfWorkHours); } @Test @Transactional public void basicTest() { givenBasicExample(); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(0)), equalTo(new BigDecimal(500).setScale(2))); } @Test @Transactional public void basicTestWithoutCostCategoryRelationship() { givenBasicExampleWithoutCostCategoryRelationship(); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(0)), equalTo(new BigDecimal(300).setScale(2))); } @Test @Transactional public void exampleOrderLineGroup() { givenExampleOrderLineGroup(); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(0)), equalTo(new BigDecimal(1500).setScale(2))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(1)), equalTo(new BigDecimal(500).setScale(2))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(2)), equalTo(new BigDecimal(500).setScale(2))); } @Test @Transactional public void exampleOrderLineGroupWithDifferentHours1() { givenExampleOrderLineGroupWithDifferentHours(Arrays.asList(0, 10, 5)); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(0)), equalTo(new BigDecimal(750).setScale(2))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(1)), equalTo(new BigDecimal(500).setScale(2))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(2)), equalTo(new BigDecimal(250).setScale(2))); } @Test @Transactional public void exampleOrderLineGroupWithDifferentHours2() { givenExampleOrderLineGroupWithDifferentHours(Arrays.asList(6, 0, 0)); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(0)), equalTo(new BigDecimal(300).setScale(2))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(1)), equalTo(new BigDecimal(0).setScale(2))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(2)), equalTo(new BigDecimal(0).setScale(2))); } @Test @Transactional public void exampleOrderLineGroupWithDifferentHours3() { givenExampleOrderLineGroupWithDifferentHours(Arrays.asList(6, 5, 10)); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(0)), equalTo(new BigDecimal(1050).setScale(2))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(1)), equalTo(new BigDecimal(250).setScale(2))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(2)), equalTo(new BigDecimal(500).setScale(2))); } @Test @Transactional public void exampleWithoutCostCategoryRelationshipButDifferentTypeOfHours1() { givenExampleWithoutCostCategoryRelationshipButDifferentTypeOfHours( Arrays.asList(10, 5), Arrays.asList(new BigDecimal(30), new BigDecimal(50))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(0)), equalTo(new BigDecimal(550).setScale(2))); } @Test @Transactional public void exampleWithoutCostCategoryRelationshipButDifferentTypeOfHours2() { givenExampleWithoutCostCategoryRelationshipButDifferentTypeOfHours( Arrays.asList(10, 5, 8), Arrays.asList(new BigDecimal(30), new BigDecimal(50), new BigDecimal(40))); assertThat(moneyCostCalculator.getHoursMoneyCost(orderElements.get(0)), equalTo(new BigDecimal(870).setScale(2))); } private ExpenseSheet givenExpenseSheet() { expenseSheet = ExpenseSheet.create(); expenseSheet.setCode("default-expense-sheet" + UUID.randomUUID()); return expenseSheet; } private ExpenseSheetLine createExpenseSheetLine(BigDecimal value, String concept, int indiceOrder) { ExpenseSheetLine expenseSheetLine = ExpenseSheetLine.create(value, concept, new LocalDate(), orderElements.get(indiceOrder)); expenseSheetLine.setCode("default-expense-sheet-line-" + UUID.randomUUID()); expenseSheetLine.setResource(resource); expenseSheetLine.setExpenseSheet(expenseSheet); expenseSheet.add(expenseSheetLine); return expenseSheetLine; } private void givenBasicExpensesExample() { givenExpenseSheet(); givenOrderElement(); givenExpenseSheetLines(0); } private void givenExpensesExampleWithChildren() { givenExpenseSheet(); givenOrderLineGroupWithTwoLines(); givenExpenseSheetLines(0); givenExpenseSheetLines(1); } private void givenExpenseSheetLines(int indiceOrder) { createExpenseSheetLine(BigDecimal.TEN, "expense-sheet-line-concept-1", indiceOrder); createExpenseSheetLine(new BigDecimal(55), "expense-sheet-line-concept-1", indiceOrder); } private void saveExpensesSheetLines() { Set<ExpenseSheetLine> deletedExpenseSheetLinesSet = new HashSet<>(); sumExpensesDAO.updateRelatedSumExpensesWithDeletedExpenseSheetLineSet(deletedExpenseSheetLinesSet); sumExpensesDAO.updateRelatedSumExpensesWithExpenseSheetLineSet(expenseSheet.getExpenseSheetLines()); sumExpensesDAO.flush(); expenseSheet.updateCalculatedProperties(); expenseSheetDAO.save(expenseSheet); expenseSheetDAO.flush(); } @Test public void testAddExpenseSheetWithTwoDirectLines() { transactionService.runOnTransaction(new IOnTransaction<Void>() { @Override public Void execute() { orderElements.clear(); givenBasicExpensesExample(); saveExpensesSheetLines(); return null; } }); transactionService.runOnTransaction(new IOnTransaction<Void>() { @Override public Void execute() { OrderElement orderElement; try { orderElement = orderElementDAO.findByCode(orderElements.get(0).getCode()); assertThat(moneyCostCalculator.getExpensesMoneyCost(orderElement), equalTo(new BigDecimal(65).setScale(2))); } catch (InstanceNotFoundException e) { e.printStackTrace(); } return null; } }); } @Test public void testAddExpenseSheetWithIndirectLines() { transactionService.runOnTransaction(new IOnTransaction<Void>() { @Override public Void execute() { orderElements.clear(); givenExpensesExampleWithChildren(); saveExpensesSheetLines(); return null; } }); transactionService.runOnTransaction(new IOnTransaction<Void>() { @Override public Void execute() { OrderElement orderElement; try { orderElement = orderElementDAO.findByCode(orderElements.get(0).getCode()); assertThat(moneyCostCalculator.getExpensesMoneyCost(orderElement), equalTo(new BigDecimal(130).setScale(2))); orderElement = orderElementDAO.findByCode(orderElements.get(1).getCode()); assertThat(moneyCostCalculator.getExpensesMoneyCost(orderElement), equalTo(new BigDecimal(65).setScale(2))); } catch (InstanceNotFoundException e) { e.printStackTrace(); } return null; } }); } }