diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DerivedAllocationGenerator.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DerivedAllocationGenerator.java new file mode 100644 index 000000000..eac5e7899 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DerivedAllocationGenerator.java @@ -0,0 +1,123 @@ +/* + * 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.planner.entities; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.joda.time.LocalDate; +import org.navalplanner.business.planner.entities.HoursDistributor.ResourceWithAssignedHours; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.Machine; +import org.navalplanner.business.resources.entities.MachineWorkerAssignment; +import org.navalplanner.business.resources.entities.MachineWorkersConfigurationUnit; +import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.business.resources.entities.Worker; + +/** + * @author Óscar González Fernández + */ +public class DerivedAllocationGenerator { + + public interface IWorkerFinder { + Collection findWorkersMatching( + Collection requiredCriterions); + } + + public static DerivedAllocation generate(ResourceAllocation derivedFrom, + IWorkerFinder finder, + MachineWorkersConfigurationUnit configurationUnit, + List dayAssignments) { + Validate.notNull(derivedFrom); + Validate.notNull(finder); + Validate.notNull(configurationUnit); + Validate.noNullElements(dayAssignments); + DerivedAllocation result = DerivedAllocation.create(derivedFrom, + configurationUnit); + List foundResources = findResources(finder, configurationUnit); + final Machine machine = configurationUnit.getMachine(); + BigDecimal alpha = configurationUnit.getAlpha(); + result.resetAssignmentsTo(createAssignments(result, alpha, + foundResources, onlyFor(machine, dayAssignments))); + return result; + } + + private static List onlyFor(Machine machine, + List dayAssignments) { + List result = new ArrayList(); + for (DayAssignment each : dayAssignments) { + if (each.isAssignedTo(machine)) { + result.add(each); + } + } + return result; + } + + private static List findResources(IWorkerFinder finder, + MachineWorkersConfigurationUnit configurationUnit) { + Set result = getResourcesFromAssignments(configurationUnit); + result.addAll(finder.findWorkersMatching(configurationUnit + .getRequiredCriterions())); + return new ArrayList(result); + } + + private static Set getResourcesFromAssignments( + MachineWorkersConfigurationUnit configurationUnit) { + Set result = new HashSet(); + for (MachineWorkerAssignment each : configurationUnit + .getWorkerAssignments()) { + result.add(each.getWorker()); + } + return result; + } + + private static List createAssignments( + DerivedAllocation parent, BigDecimal alpha, + List resourcesFound, + List dayAssignments) { + List result = new ArrayList(); + HoursDistributor distributor = new HoursDistributor(resourcesFound); + for (DayAssignment each : dayAssignments) { + BigDecimal hours = alpha.multiply(new BigDecimal(each.getHours())); + LocalDate day = each.getDay(); + List distributeForDay = distributor + .distributeForDay(day, hours.intValue()); + result.addAll(asDerived(parent, day, distributeForDay)); + } + return result; + } + + private static List asDerived( + DerivedAllocation parent, LocalDate day, + List distributeForDay) { + List result = new ArrayList(); + for (ResourceWithAssignedHours each : distributeForDay) { + result.add(DerivedDayAssignment.create(day, each.hours, + each.resource, parent)); + } + return result; + } + +} diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/DerivedAllocationGeneratorTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/DerivedAllocationGeneratorTest.java new file mode 100644 index 000000000..4ee38d633 --- /dev/null +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/DerivedAllocationGeneratorTest.java @@ -0,0 +1,247 @@ +/* + * 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.test.planner.entities; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.isA; +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.navalplanner.business.test.planner.entities.DayAssignmentMatchers.haveHours; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.junit.Test; +import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.DerivedAllocation; +import org.navalplanner.business.planner.entities.DerivedAllocationGenerator; +import org.navalplanner.business.planner.entities.DerivedDayAssignment; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.DerivedAllocationGenerator.IWorkerFinder; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.Machine; +import org.navalplanner.business.resources.entities.MachineWorkerAssignment; +import org.navalplanner.business.resources.entities.MachineWorkersConfigurationUnit; +import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.business.resources.entities.Worker; + +/** + * @author Óscar González Fernández + */ +public class DerivedAllocationGeneratorTest { + + private Machine machine = null; + + private ResourceAllocation derivedFrom; + private IWorkerFinder finder; + private MachineWorkersConfigurationUnit configurationUnit; + private List dayAssignments; + + private void givenDerivedFrom() { + ResourceAllocation result = createNiceMock(ResourceAllocation.class); + replay(result); + derivedFrom = result; + } + + @SuppressWarnings("unchecked") + private void givenFinder(Worker... workers) { + IWorkerFinder result = createNiceMock(IWorkerFinder.class); + Collection argument = (Collection) anyObject(); + expect(result.findWorkersMatching(argument)).andReturn( + Arrays.asList(workers)).anyTimes(); + replay(result); + finder = result; + } + + private void givenConfigurationUnit() { + MachineWorkersConfigurationUnit result = createNiceMock(MachineWorkersConfigurationUnit.class); + replay(result); + configurationUnit = result; + } + + private void givenConfigurationUnit(BigDecimal alpha, Worker... workers) { + MachineWorkersConfigurationUnit result = createNiceMock(MachineWorkersConfigurationUnit.class); + expect(result.getAlpha()).andReturn(alpha).anyTimes(); + expect(result.getWorkerAssignments()).andReturn( + assignmentsFor(result, workers)).anyTimes(); + expect(result.getMachine()).andReturn(machine).anyTimes(); + replay(result); + configurationUnit = result; + } + + private Set assignmentsFor( + MachineWorkersConfigurationUnit unit, Worker[] workers) { + Set result = new HashSet(); + for (Worker each : workers) { + MachineWorkerAssignment assignment = workerAssignment(unit,each); + result.add(assignment); + } + return result; + } + + private MachineWorkerAssignment workerAssignment( + MachineWorkersConfigurationUnit unit, Worker each) { + MachineWorkerAssignment result = createNiceMock(MachineWorkerAssignment.class); + expect(result.getMachineWorkersConfigurationUnit()).andReturn(unit) + .anyTimes(); + expect(result.getWorker()).andReturn(each); + expect(result.getStartDate()).andReturn( + asDate(new LocalDate(2000, 1, 1))).anyTimes(); + expect(result.getFinishDate()).andReturn(null).anyTimes(); + replay(result); + return result; + } + + private Worker workerWithAlwaysAssignedHours(int assignedHours){ + Worker result = createNiceMock(Worker.class); + expect(result.getAssignedHours(isA(LocalDate.class))).andReturn( + assignedHours).anyTimes(); + replay(result); + return result; + } + + private Date asDate(LocalDate localDate) { + return localDate.toDateTimeAtStartOfDay().toDate(); + } + + private void givenDayAssignments() { + dayAssignments = new ArrayList(); + } + + private void givenDayAssignments(LocalDate start, int... hours) { + dayAssignments = new ArrayList(); + for (int i = 0; i < hours.length; i++) { + dayAssignments.add(createAssignment(start.plusDays(i), machine, + hours[i])); + } + } + + private DayAssignment createAssignment(LocalDate day, Machine machine, + int hours) { + DayAssignment dayAssignment = createNiceMock(DayAssignment.class); + expect(dayAssignment.getHours()).andReturn(hours).anyTimes(); + expect(dayAssignment.getResource()).andReturn(machine).anyTimes(); + expect(dayAssignment.getDay()).andReturn(day).anyTimes(); + expect(dayAssignment.isAssignedTo(machine)).andReturn(true).anyTimes(); + replay(dayAssignment); + return dayAssignment; + } + + @Test(expected = IllegalArgumentException.class) + public void derivedFromMustBeNotNull() { + givenFinder(); + givenConfigurationUnit(); + givenDayAssignments(); + DerivedAllocationGenerator.generate(derivedFrom, finder, + configurationUnit, dayAssignments); + } + + @Test(expected = IllegalArgumentException.class) + public void finderMustBeNotNull() { + givenDerivedFrom(); + givenConfigurationUnit(); + givenDayAssignments(); + DerivedAllocationGenerator.generate(derivedFrom, finder, + configurationUnit, dayAssignments); + } + + @Test(expected = IllegalArgumentException.class) + public void configurationUnitMustBeNotNull() { + givenDerivedFrom(); + givenFinder(); + givenDayAssignments(); + DerivedAllocationGenerator.generate(derivedFrom, finder, + configurationUnit, dayAssignments); + } + + @Test(expected = IllegalArgumentException.class) + public void dayAssignmentsMustBeNotNull() { + givenDerivedFrom(); + givenFinder(); + givenConfigurationUnit(); + DerivedAllocationGenerator.generate(derivedFrom, finder, + configurationUnit, dayAssignments); + } + + @Test + public void forOneResourceTheHoursGeneratedAreGotFromAlpha() { + givenDerivedFrom(); + givenFinder(); + givenConfigurationUnit(new BigDecimal(1.5), new Worker()); + givenDayAssignments(new LocalDate(2009, 10, 20), 8, 8, 8, 4); + DerivedAllocation derivedAllocation = DerivedAllocationGenerator + .generate(derivedFrom, finder, configurationUnit, + dayAssignments); + List assignments = derivedAllocation + .getAssignments(); + assertThat(assignments, haveHours(12, 12, 12, 6)); + } + + @Test + public void onlyDayAssignmentsForTheMachineOfTheConfigurationUnitAreUsed() { + givenDerivedFrom(); + givenFinder(); + givenConfigurationUnit(new BigDecimal(1.5), new Worker()); + LocalDate start = new LocalDate(2009, 10, 20); + givenDayAssignments(start, 8, 8, 8, 4); + + Machine otherMachine = Machine.create(); + dayAssignments + .add(createAssignment(start.plusDays(5), otherMachine, 8)); + + DerivedAllocation derivedAllocation = DerivedAllocationGenerator + .generate(derivedFrom, finder, configurationUnit, + dayAssignments); + List assignments = derivedAllocation + .getAssignments(); + assertThat(assignments.size(), equalTo(4)); + } + + @Test + public void forSeveralResourcesTheHoursAreDistributedTakingIntoAccountTheFreeHours() { + givenDerivedFrom(); + Worker worker1 = workerWithAlwaysAssignedHours(4); + Worker worker2 = workerWithAlwaysAssignedHours(6); + givenFinder(worker1, worker2); + givenConfigurationUnit(new BigDecimal(1.5)); + givenDayAssignments(new LocalDate(2009, 10, 20), 8, 8, 8, 4); + DerivedAllocation derivedAllocation = DerivedAllocationGenerator + .generate(derivedFrom, finder, configurationUnit, + dayAssignments); + List assignments = derivedAllocation + .getAssignments(); + Map> byResource = DayAssignment + .byResourceAndOrdered(assignments); + assertThat(byResource.get(worker1), haveHours(7, 7, 7, 4)); + assertThat(byResource.get(worker2), haveHours(5, 5, 5, 2)); + } + +}