From 6af12a9eba0396bb65efb30bb89b3bae274095d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Mon, 25 May 2009 18:45:58 +0200 Subject: [PATCH] ItEr09S09AdministracionGrupos: Assignment of worker to localization groups. It uses a subcontroller for managing localizations. Worker controller and related model classes are moved to another package. Javier Moran Rua Deleted duplicated files. --- .../resources/entities/Criterion.java | 4 +- .../business/resources/entities/Resource.java | 16 + .../resources/services/CriterionService.java | 6 + .../services/impl/CriterionServiceImpl.java | 5 + .../test/resources/entities/ResourceTest.java | 37 ++- .../services/CriterionServiceTest.java | 66 +++- .../org/navalplanner/web/common/Util.java | 10 +- .../web/resources/WorkerModel.java | 78 ----- .../IMultipleCriterionActiveAssigner.java | 24 ++ .../resources/{ => worker}/IWorkerModel.java | 13 +- .../worker/LocalizationsController.java | 94 ++++++ .../WorkRelationshipsController.java | 9 +- .../{ => worker}/WorkerCRUDController.java | 36 ++- .../web/resources/worker/WorkerModel.java | 287 ++++++++++++++++++ .../navalplanner-webapp-spring-config.xml | 3 - .../main/webapp/resources/worker/_edition.zul | 77 +++-- .../resources/worker/_localizations.zul | 46 +++ .../main/webapp/resources/worker/worker.zul | 2 +- .../resources/WorkerCRUDControllerTest.java | 2 + .../web/resources/WorkerModelTest.java | 30 +- 20 files changed, 686 insertions(+), 159 deletions(-) delete mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkerModel.java create mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/IMultipleCriterionActiveAssigner.java rename navalplanner-webapp/src/main/java/org/navalplanner/web/resources/{ => worker}/IWorkerModel.java (76%) create mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/LocalizationsController.java rename navalplanner-webapp/src/main/java/org/navalplanner/web/resources/{ => worker}/WorkRelationshipsController.java (77%) rename navalplanner-webapp/src/main/java/org/navalplanner/web/resources/{ => worker}/WorkerCRUDController.java (73%) create mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerModel.java create mode 100644 navalplanner-webapp/src/main/webapp/resources/worker/_localizations.zul diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java index 0c39f2db9..868fa6b95 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java @@ -86,8 +86,8 @@ public class Criterion implements ICriterion { public boolean isEquivalent(ICriterion criterion) { if (criterion instanceof Criterion) { Criterion other = (Criterion) criterion; - return new EqualsBuilder().append(name, other.name).append(type, - other.type).isEquals(); + return new EqualsBuilder().append(getName(), other.getName()) + .append(getType(), other.getType()).isEquals(); } return false; } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Resource.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Resource.java index a738bd610..993bb1758 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Resource.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Resource.java @@ -37,6 +37,13 @@ public abstract class Resource { return id; } + public void forceLoadSatisfactions() { + for (CriterionSatisfaction criterionSatisfaction : criterionSatisfactions) { + criterionSatisfaction.getCriterion().getName(); + criterionSatisfaction.getCriterion().getType(); + } + } + public abstract int getDailyCapacity(); public long getVersion() { @@ -84,6 +91,15 @@ public abstract class Resource { return result; } + public Set getActiveCriterionsFor(ICriterionType type) { + Set result = new HashSet(); + Collection active = getActiveSatisfactionsFor(type); + for (CriterionSatisfaction satisfaction : active) { + result.add(satisfaction.getCriterion()); + } + return result; + } + public Collection getActiveSatisfactionsFor( ICriterionType criterionType) { Collection satisfactionsFor = getSatisfactionsFor(criterionType); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/CriterionService.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/CriterionService.java index 21e9c0261..7131e5882 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/CriterionService.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/CriterionService.java @@ -48,4 +48,10 @@ public interface CriterionService { Criterion load(Criterion criterion); + interface OnTransaction { + public T execute(); + } + + T onTransaction(OnTransaction onTransaction); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/CriterionServiceImpl.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/CriterionServiceImpl.java index 6dd7183f4..992c15660 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/CriterionServiceImpl.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/CriterionServiceImpl.java @@ -192,4 +192,9 @@ public class CriterionServiceImpl implements CriterionService { } } + @Transactional(readOnly = true) + public T onTransaction(OnTransaction onTransaction) { + return onTransaction.execute(); + } + } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/ResourceTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/ResourceTest.java index df5790d56..4351ebac3 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/ResourceTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/ResourceTest.java @@ -1,12 +1,5 @@ package org.navalplanner.business.test.resources.entities; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.util.Arrays; import java.util.HashSet; @@ -22,6 +15,13 @@ import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.business.test.resources.daos.CriterionDAOTest; import org.navalplanner.business.test.resources.daos.CriterionSatisfactionDAOTest; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + /** * Tests for {@link Resource}.
* @author Óscar González Fernández @@ -81,6 +81,29 @@ public class ResourceTest { assertEquals(1, worker.getActiveSatisfactionsFor(criterionType).size()); } + @Test + public void testActiveCriterions() throws Exception { + final Criterion criterion = CriterionDAOTest.createValidCriterion(); + Criterion otherCriterion = CriterionDAOTest.createValidCriterion(); + ICriterionType type = createTypeThatMatches(criterion, + otherCriterion); + CriterionWithItsType criterionWithItsType = new CriterionWithItsType( + type, criterion); + CriterionWithItsType otherCriterionWithItsType = new CriterionWithItsType( + type, otherCriterion); + Worker worker = new Worker("firstName", "surName", "2333232", 10); + assertThat(worker.getActiveCriterionsFor(type).size(), equalTo(0)); + worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest + .year(2000)); + assertThat(worker.getActiveCriterionsFor(type).size(), equalTo(1)); + worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest + .year(2002)); + assertThat(worker.getActiveCriterionsFor(type).size(), equalTo(1)); + worker.activate(otherCriterionWithItsType, CriterionSatisfactionDAOTest + .year(2000)); + assertThat(worker.getActiveCriterionsFor(type).size(), equalTo(2)); + } + public static CriterionTypeBase createTypeThatMatches( final Criterion... criterions) { return createTypeThatMatches(true, criterions); diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/CriterionServiceTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/CriterionServiceTest.java index 735a2e5c5..bee04a9b6 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/CriterionServiceTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/CriterionServiceTest.java @@ -1,22 +1,16 @@ package org.navalplanner.business.test.resources.services; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; -import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; - import java.util.Collection; import java.util.UUID; import org.hibernate.SessionFactory; import org.hibernate.validator.InvalidStateException; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; -import org.navalplanner.business.resources.daos.impl.CriterionDAO; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.CriterionWithItsType; import org.navalplanner.business.resources.entities.ICriterion; import org.navalplanner.business.resources.entities.ICriterionOnData; @@ -26,6 +20,7 @@ import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.business.resources.services.CriterionService; import org.navalplanner.business.resources.services.ResourceService; +import org.navalplanner.business.resources.services.CriterionService.OnTransaction; import org.navalplanner.business.test.resources.daos.CriterionDAOTest; import org.navalplanner.business.test.resources.daos.CriterionSatisfactionDAOTest; import org.navalplanner.business.test.resources.entities.ResourceTest; @@ -36,6 +31,14 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; +import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; + /** * Test cases for {@link CriterionService}
* @author Óscar González Fernández @@ -55,10 +58,6 @@ public class CriterionServiceTest { @Autowired private SessionFactory sessionFactory; - @Autowired - private CriterionDAO criterionDAO; - - @Test(expected = InvalidStateException.class) public void testCantSaveCriterionWithoutNameAndType() throws Exception { Criterion criterion = Criterion.withNameAndType("valido", "valido"); @@ -84,8 +83,7 @@ public class CriterionServiceTest { int initial = criterionService.getCriterionsFor( PredefinedCriterionTypes.WORK_RELATIONSHIP).size(); criterionService.save(criterion); - assertThat("after saving one more", criterionService - .getCriterionsFor( + assertThat("after saving one more", criterionService.getCriterionsFor( PredefinedCriterionTypes.WORK_RELATIONSHIP).size(), equalTo(initial + 1)); criterion.setActive(false); @@ -224,6 +222,42 @@ public class CriterionServiceTest { CriterionSatisfactionDAOTest.year(2005)).size()); } + @Test + @NotTransactional + public void testCriterionIsEquivalentOnDetachedAndProxifiedCriterion() + throws Exception { + final Worker worker1 = new Worker("worker-1", "worker-2-surname", + "11111111A", 8); + resourceService.saveResource(worker1); + Criterion criterion = CriterionDAOTest.createValidCriterion(); + criterionService.save(criterion); + ICriterionType type = createTypeThatMatches(criterion); + worker1.activate(new CriterionWithItsType(type, criterion)); + resourceService.saveResource(worker1); + Resource workerReloaded = criterionService + .onTransaction(new OnTransaction() { + + @Override + public Resource execute() { + try { + Resource result = resourceService + .findResource(worker1.getId()); + result.forceLoadSatisfactions(); + return result; + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + }); + Collection satisfactionsFor = workerReloaded + .getSatisfactionsFor(type); + Criterion reloadedCriterion = satisfactionsFor.iterator().next() + .getCriterion(); + Assume.assumeTrue(!reloadedCriterion.getClass().equals( + criterion.getClass())); + assertTrue(reloadedCriterion.isEquivalent(criterion)); + } + @Test @NotTransactional public void shouldntThrowExceptionDueToTransparentProxyGotcha() { @@ -345,7 +379,7 @@ public class CriterionServiceTest { @Override public boolean contains(ICriterion c) { - return criterion.equals(c); + return criterion.isEquivalent(c); } @Override diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/Util.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/Util.java index 8ad475500..30a7ef536 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/Util.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/Util.java @@ -9,10 +9,12 @@ import org.zkoss.zkplus.databind.DataBinder; */ public class Util { - public static void reloadBindings(Component reload) { - DataBinder binder = Util.getBinder(reload); - if (binder != null) { - binder.loadComponent(reload); + public static void reloadBindings(Component... toReload) { + for (Component reload : toReload) { + DataBinder binder = Util.getBinder(reload); + if (binder != null) { + binder.loadComponent(reload); + } } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkerModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkerModel.java deleted file mode 100644 index 05c3e7dca..000000000 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkerModel.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.navalplanner.web.resources; - -import java.util.List; - -import org.apache.commons.lang.Validate; -import org.hibernate.validator.ClassValidator; -import org.hibernate.validator.InvalidValue; -import org.navalplanner.business.common.exceptions.InstanceNotFoundException; -import org.navalplanner.business.common.exceptions.ValidationException; -import java.util.Set; -import org.navalplanner.business.resources.entities.CriterionSatisfaction; -import org.navalplanner.business.resources.entities.Worker; -import org.navalplanner.business.resources.services.ResourceService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Transactional; - -/** - * Model for worker
- * @author Óscar González Fernández - */ -public class WorkerModel implements IWorkerModel { - - private final ResourceService resourceService; - private Worker worker; - private ClassValidator workerValidator; - - @Autowired - public WorkerModel(ResourceService resourceService) { - if (resourceService == null) - throw new IllegalArgumentException("resourceService cannot be null"); - this.resourceService = resourceService; - this.workerValidator = new ClassValidator(Worker.class); - } - - @Override - public void save() throws ValidationException { - InvalidValue[] invalidValues = workerValidator - .getInvalidValues(getWorker()); - if (invalidValues.length > 0) { - throw new ValidationException(invalidValues); - } - resourceService.saveResource(worker); - } - - @Override - public List getWorkers() { - return resourceService.getWorkers(); - } - - @Override - - public Worker getWorker() { - return worker; - } - - @Override - public void prepareForCreate() { - worker = new Worker(); - } - - @Override - @Transactional(readOnly=true) - public void prepareEditFor(Worker worker) { - Validate.notNull(worker, "worker is not null"); - try { - this.worker = (Worker) resourceService.findResource(worker.getId()); - } catch (InstanceNotFoundException e) { - throw new RuntimeException(e); - } - this.worker.getAllSatisfactions(); - for ( CriterionSatisfaction cs : this.worker.getAllSatisfactions() ) {} - } - - public Set getCriterionSatisfactions(Worker worker) { - return worker.getAllSatisfactions(); - } - -} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/IMultipleCriterionActiveAssigner.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/IMultipleCriterionActiveAssigner.java new file mode 100644 index 000000000..482cdc956 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/IMultipleCriterionActiveAssigner.java @@ -0,0 +1,24 @@ +package org.navalplanner.web.resources.worker; + +import java.util.Collection; +import java.util.List; + +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionSatisfaction; + +public interface IMultipleCriterionActiveAssigner { + + public List getHistoric(); + + public List getActiveSatisfactions(); + + public List getCriterionsNotAssigned(); + + public void unassign( + Collection satisfactions); + + public void assign(Collection criterions); + + public void applyChanges(); + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/IWorkerModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/IWorkerModel.java similarity index 76% rename from navalplanner-webapp/src/main/java/org/navalplanner/web/resources/IWorkerModel.java rename to navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/IWorkerModel.java index 2f7e0159a..ca545481e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/IWorkerModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/IWorkerModel.java @@ -1,14 +1,14 @@ -package org.navalplanner.web.resources; +package org.navalplanner.web.resources.worker; import java.util.List; - import java.util.Set; + import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.Worker; /** - * Interface for workerModel.
+ * Interface for {@link WorkerModel}.
* @author Óscar González Fernández */ public interface IWorkerModel { @@ -23,5 +23,10 @@ public interface IWorkerModel { void prepareEditFor(Worker worker); + IMultipleCriterionActiveAssigner getLocalizationsAssigner(); + + boolean isCreating(); + Set getCriterionSatisfactions(Worker worker); -} + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/LocalizationsController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/LocalizationsController.java new file mode 100644 index 000000000..15960fe22 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/LocalizationsController.java @@ -0,0 +1,94 @@ +package org.navalplanner.web.resources.worker; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionSatisfaction; +import org.navalplanner.web.common.Util; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.api.Button; +import org.zkoss.zul.api.Listbox; +import org.zkoss.zul.api.Listitem; + +/** + * Subcontroller for assigning localizations
+ * @author Óscar González Fernández + */ +public class LocalizationsController extends GenericForwardComposer { + + private IWorkerModel workerModel; + + private Listbox activeSatisfactions; + + private Listbox criterionsNotAssigned; + + private Button unassignButton; + + private Button assignButton; + + LocalizationsController(IWorkerModel workerModel) { + Validate.notNull(workerModel); + this.workerModel = workerModel; + } + + public List getLocalizationsHistory() { + return workerModel.getLocalizationsAssigner().getHistoric(); + } + + public List getActiveSatisfactions() { + return workerModel.getLocalizationsAssigner().getActiveSatisfactions(); + } + + public List getCriterionsNotAssigned() { + return workerModel.getLocalizationsAssigner() + .getCriterionsNotAssigned(); + } + + private void reloadLists() { + Util.reloadBindings(activeSatisfactions, criterionsNotAssigned); + } + + private static List extractValuesOf( + Collection items, Class klass) { + ArrayList result = new ArrayList(); + for (Listitem listitem : items) { + result.add(klass.cast(listitem.getValue())); + } + return result; + } + + @Override + public void doAfterCompose(Component comp) throws Exception { + super.doAfterCompose(comp); + unassignButton.addEventListener("onClick", new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + workerModel.getLocalizationsAssigner().unassign( + extractValuesOf(activeSatisfactions.getSelectedItems(), + CriterionSatisfaction.class)); + reloadLists(); + } + + }); + assignButton.addEventListener("onClick", new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + Set selectedItems = criterionsNotAssigned + .getSelectedItems(); + workerModel.getLocalizationsAssigner().assign( + extractValuesOf(selectedItems, Criterion.class)); + reloadLists(); + } + }); + } + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkRelationshipsController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkRelationshipsController.java similarity index 77% rename from navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkRelationshipsController.java rename to navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkRelationshipsController.java index 1fc24507f..1b5aff7a7 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkRelationshipsController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkRelationshipsController.java @@ -1,8 +1,10 @@ -package org.navalplanner.web.resources; +package org.navalplanner.web.resources.worker; import java.util.HashSet; import java.util.Set; + import org.navalplanner.business.resources.entities.CriterionSatisfaction; +import org.navalplanner.business.resources.entities.Worker; import org.zkoss.zk.ui.util.GenericForwardComposer; /** @@ -24,8 +26,9 @@ public class WorkRelationshipsController extends GenericForwardComposer { if (this.workerCRUDController.getWorker() == null) { return new HashSet(); } else { - return workerModel.getCriterionSatisfactions( - this.workerCRUDController.getWorker()); + return workerModel + .getCriterionSatisfactions(this.workerCRUDController + .getWorker()); } } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkerCRUDController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerCRUDController.java similarity index 73% rename from navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkerCRUDController.java rename to navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerCRUDController.java index e5f045e82..35e860f04 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/WorkerCRUDController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerCRUDController.java @@ -1,12 +1,9 @@ -package org.navalplanner.web.resources; +package org.navalplanner.web.resources.worker; import java.util.List; -import java.util.Set; import org.hibernate.validator.InvalidValue; import org.navalplanner.business.common.exceptions.ValidationException; -import org.navalplanner.business.resources.entities.Criterion; -import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.web.common.IMessagesForUser; import org.navalplanner.web.common.Level; @@ -41,6 +38,10 @@ public class WorkerCRUDController extends GenericForwardComposer { private GenericForwardComposer workRelationship; + private LocalizationsController localizationsForEditionController; + + private LocalizationsController localizationsForCreationController; + public WorkerCRUDController() { } @@ -63,6 +64,12 @@ public class WorkerCRUDController extends GenericForwardComposer { return workerModel.getWorkers(); } + public LocalizationsController getLocalizations() { + if (workerModel.isCreating()) + return localizationsForCreationController; + return localizationsForEditionController; + } + public void save() { try { workerModel.save(); @@ -95,24 +102,39 @@ public class WorkerCRUDController extends GenericForwardComposer { workerModel.prepareForCreate(); getVisibility().showOnly(createWindow); Util.reloadBindings(createWindow); + this.workRelationship = new WorkRelationshipsController( + this.workerModel, this); + } @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); + localizationsForEditionController = createLocalizationsController(comp, + "editWindow"); + localizationsForCreationController = createLocalizationsController( + comp, "createWindow"); comp.setVariable("controller", this, true); getVisibility().showOnly(listWindow); if (messagesContainer == null) throw new RuntimeException("messagesContainer is needed"); messages = new MessagesForUser(messagesContainer); - this.workRelationship = - new WorkRelationshipsController(this.workerModel,this); + } + + private LocalizationsController createLocalizationsController( + Component comp, String localizationsContainerName) throws Exception { + LocalizationsController localizationsController = new LocalizationsController( + workerModel); + localizationsController + .doAfterCompose(comp.getFellow(localizationsContainerName) + .getFellow("localizationsContainer")); + return localizationsController; } private OnlyOneVisible getVisibility() { if (visibility == null) { visibility = new OnlyOneVisible(listWindow, editWindow, - createWindow, workRelationshipsWindow ); + createWindow, workRelationshipsWindow); } return visibility; } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerModel.java new file mode 100644 index 000000000..66c412e53 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerModel.java @@ -0,0 +1,287 @@ +package org.navalplanner.web.resources.worker; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.hibernate.validator.ClassValidator; +import org.hibernate.validator.InvalidValue; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionSatisfaction; +import org.navalplanner.business.resources.entities.CriterionWithItsType; +import org.navalplanner.business.resources.entities.ICriterionType; +import org.navalplanner.business.resources.entities.PredefinedCriterionTypes; +import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.business.resources.entities.Worker; +import org.navalplanner.business.resources.services.CriterionService; +import org.navalplanner.business.resources.services.ResourceService; +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; +import org.springframework.transaction.annotation.Transactional; + +/** + * Model for worker
+ * @author Óscar González Fernández + */ +@Component +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class WorkerModel implements IWorkerModel { + + private final ResourceService resourceService; + private Worker worker; + private ClassValidator workerValidator; + private final CriterionService criterionService; + + private IMultipleCriterionActiveAssigner localizationsAssigner; + + @Autowired + public WorkerModel(ResourceService resourceService, + CriterionService criterionService) { + Validate.notNull(resourceService); + Validate.notNull(criterionService); + this.resourceService = resourceService; + this.workerValidator = new ClassValidator(Worker.class); + this.criterionService = criterionService; + } + + @Override + public void save() throws ValidationException { + InvalidValue[] invalidValues = workerValidator + .getInvalidValues(getWorker()); + if (invalidValues.length > 0) { + throw new ValidationException(invalidValues); + } + getLocalizationsAssigner().applyChanges(); + resourceService.saveResource(worker); + worker = null; + localizationsAssigner = null; + } + + @Override + public List getWorkers() { + return resourceService.getWorkers(); + } + + @Override + public Worker getWorker() { + return worker; + } + + @Override + @Transactional(readOnly = true) + public void prepareForCreate() { + worker = new Worker(); + localizationsAssigner = new MultipleCriterionActiveAssigner( + criterionService, worker, + PredefinedCriterionTypes.LOCATION_GROUP); + } + + @Override + @Transactional(readOnly = true) + public void prepareEditFor(Worker worker) { + Validate.notNull(worker, "worker is not null"); + try { + this.worker = (Worker) resourceService.findResource(worker.getId()); + this.worker.forceLoadSatisfactions(); + localizationsAssigner = new MultipleCriterionActiveAssigner( + criterionService, this.worker, + PredefinedCriterionTypes.LOCATION_GROUP); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + + public Set getCriterionSatisfactions(Worker worker) { + return worker.getAllSatisfactions(); + } + + private static class NullAssigner implements + IMultipleCriterionActiveAssigner { + + private List empty = Arrays.asList(); + + private List emptyCriterions = Arrays.asList(); + + @Override + public void assign(Collection criterions) { + + } + + @Override + public List getActiveSatisfactions() { + return empty; + } + + @Override + public List getCriterionsNotAssigned() { + return emptyCriterions; + } + + @Override + public List getHistoric() { + return empty; + } + + @Override + public void unassign( + Collection satisfactions) { + } + + @Override + public void applyChanges() { + } + + } + + private static class MultipleCriterionActiveAssigner implements + IMultipleCriterionActiveAssigner { + private final Resource resource; + private final ICriterionType type; + private final CriterionService criterionService; + private List history; + private List initialCriterionsNotAssigned; + private Set initialActive; + + private Map unassigned = new HashMap(); + + private Set added = new HashSet(); + + public MultipleCriterionActiveAssigner( + CriterionService criterionService, Resource resource, + ICriterionType type) { + Validate + .isTrue( + type.allowMultipleActiveCriterionsPerResource(), + "must allow multiple active criterions for this type to use this assignment strategy"); + this.criterionService = criterionService; + this.resource = resource; + this.type = type; + this.resource.forceLoadSatisfactions(); + this.history = calculateInitialHistory(); + this.initialCriterionsNotAssigned = calculateInitialCriterionsNotAssigned(); + for (Criterion criterion : initialCriterionsNotAssigned) { + unassigned.put(criterion, createSatisfactionFor(criterion)); + } + this.initialActive = calculateInitialActive(); + } + + public List getHistoric() { + return history; + } + + private List calculateInitialHistory() { + Collection allSatisfactions = resource + .getSatisfactionsFor(type); + ArrayList result = new ArrayList(); + for (CriterionSatisfaction criterionSatisfaction : allSatisfactions) { + if (criterionSatisfaction.isFinished()) { + result.add(criterionSatisfaction); + } + } + return result; + } + + private HashSet calculateInitialActive() { + return new HashSet(resource + .getActiveSatisfactionsFor(type)); + } + + private List calculateInitialCriterionsNotAssigned() { + Map allCriterions = byId(criterionService + .getCriterionsFor(type)); + for (Long activeId : asIds(resource.getActiveCriterionsFor(type))) { + allCriterions.remove(activeId); + } + return new ArrayList(allCriterions.values()); + } + + public List getActiveSatisfactions() { + Set result = new HashSet( + added); + for (CriterionSatisfaction criterionSatisfaction : initialActive) { + if (!unassigned.containsKey(criterionSatisfaction + .getCriterion())) { + result.add(criterionSatisfaction); + } + } + return new ArrayList(result); + } + + public List getCriterionsNotAssigned() { + return new ArrayList(unassigned.keySet()); + } + + public void unassign( + Collection satisfactions) { + for (CriterionSatisfaction criterionSatisfaction : satisfactions) { + unassigned.put(criterionSatisfaction.getCriterion(), + criterionSatisfaction); + added.remove(criterionSatisfaction); + } + } + + public void assign(Collection criterions) { + for (Criterion criterion : criterions) { + CriterionSatisfaction removed = unassigned.remove(criterion); + if (!initialActive.contains(removed)) { + added.add(removed); + } + } + } + + private CriterionSatisfaction createSatisfactionFor(Criterion criterion) { + return new CriterionSatisfaction(new Date(), criterion, resource); + } + + @Override + public void applyChanges() { + for (CriterionSatisfaction criterionSatisfaction : added) { + resource.activate(new CriterionWithItsType(type, + criterionSatisfaction.getCriterion()), + criterionSatisfaction.getStartDate()); + } + for (Criterion criterion : unassigned.keySet()) { + resource.deactivate(new CriterionWithItsType(type, criterion)); + } + } + } + + public IMultipleCriterionActiveAssigner getLocalizationsAssigner() { + return localizationsAssigner != null ? localizationsAssigner + : new NullAssigner(); + } + + private static List asIds(Collection criterions) { + List result = new ArrayList(); + for (Criterion criterion : criterions) { + result.add(criterion.getId()); + } + return result; + } + + private static Map byId( + Collection criterions) { + Map result = new HashMap(); + for (Criterion criterion : criterions) { + result.put(criterion.getId(), criterion); + } + return result; + } + + @Override + public boolean isCreating() { + return worker != null && worker.getId() == null; + } + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml index e9e243690..025f1c450 100644 --- a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml +++ b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml @@ -15,7 +15,4 @@ - - \ No newline at end of file diff --git a/navalplanner-webapp/src/main/webapp/resources/worker/_edition.zul b/navalplanner-webapp/src/main/webapp/resources/worker/_edition.zul index 1d805e751..b07dbbd14 100644 --- a/navalplanner-webapp/src/main/webapp/resources/worker/_edition.zul +++ b/navalplanner-webapp/src/main/webapp/resources/worker/_edition.zul @@ -1,33 +1,50 @@ + - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/navalplanner-webapp/src/main/webapp/resources/worker/_localizations.zul b/navalplanner-webapp/src/main/webapp/resources/worker/_localizations.zul new file mode 100644 index 000000000..d9dba6d54 --- /dev/null +++ b/navalplanner-webapp/src/main/webapp/resources/worker/_localizations.zul @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/navalplanner-webapp/src/main/webapp/resources/worker/worker.zul b/navalplanner-webapp/src/main/webapp/resources/worker/worker.zul index c1b895709..343413d2b 100644 --- a/navalplanner-webapp/src/main/webapp/resources/worker/worker.zul +++ b/navalplanner-webapp/src/main/webapp/resources/worker/worker.zul @@ -10,7 +10,7 @@ diff --git a/navalplanner-webapp/src/test/java/org/navalplanner/web/resources/WorkerCRUDControllerTest.java b/navalplanner-webapp/src/test/java/org/navalplanner/web/resources/WorkerCRUDControllerTest.java index 932801864..cd4187039 100644 --- a/navalplanner-webapp/src/test/java/org/navalplanner/web/resources/WorkerCRUDControllerTest.java +++ b/navalplanner-webapp/src/test/java/org/navalplanner/web/resources/WorkerCRUDControllerTest.java @@ -17,6 +17,8 @@ import org.junit.Test; import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.web.common.IMessagesForUser; import org.navalplanner.web.common.Level; +import org.navalplanner.web.resources.worker.IWorkerModel; +import org.navalplanner.web.resources.worker.WorkerCRUDController; import org.zkoss.zul.api.Window; /** diff --git a/navalplanner-webapp/src/test/java/org/navalplanner/web/resources/WorkerModelTest.java b/navalplanner-webapp/src/test/java/org/navalplanner/web/resources/WorkerModelTest.java index bba842eb3..f35f244cf 100644 --- a/navalplanner-webapp/src/test/java/org/navalplanner/web/resources/WorkerModelTest.java +++ b/navalplanner-webapp/src/test/java/org/navalplanner/web/resources/WorkerModelTest.java @@ -4,11 +4,18 @@ import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; +import java.util.ArrayList; +import java.util.Collection; + import org.junit.Test; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.PredefinedCriterionTypes; import org.navalplanner.business.resources.entities.Worker; +import org.navalplanner.business.resources.services.CriterionService; import org.navalplanner.business.resources.services.ResourceService; +import org.navalplanner.web.resources.worker.WorkerModel; /** * Some test cases for {@link WorkerModel}.
@@ -20,18 +27,26 @@ public class WorkerModelTest { public void testWorkerValid() throws ValidationException, InstanceNotFoundException { ResourceService resourceServiceMock = createMock(ResourceService.class); - WorkerModel workerModel = new WorkerModel(resourceServiceMock); + CriterionService criterionServiceMock = createMock(CriterionService.class); Worker workerToReturn = new Worker(); workerToReturn.setDailyHours(2); workerToReturn.setFirstName("firstName"); workerToReturn.setSurname("surname"); workerToReturn.setNif("232344243"); // expectations + Collection criterions = new ArrayList(); + expect( + criterionServiceMock + .getCriterionsFor(PredefinedCriterionTypes.LOCATION_GROUP)) + .andReturn(criterions).anyTimes(); expect(resourceServiceMock.findResource(workerToReturn.getId())) .andReturn(workerToReturn); resourceServiceMock.saveResource(workerToReturn); - replay(resourceServiceMock); + replay(resourceServiceMock, criterionServiceMock); // perform actions + WorkerModel workerModel = new WorkerModel(resourceServiceMock, + criterionServiceMock); + workerModel.prepareEditFor(workerToReturn); workerModel.save(); } @@ -40,13 +55,20 @@ public class WorkerModelTest { public void testWorkerInvalid() throws ValidationException, InstanceNotFoundException { ResourceService resourceServiceMock = createMock(ResourceService.class); - WorkerModel workerModel = new WorkerModel(resourceServiceMock); + CriterionService criterionServiceMock = createMock(CriterionService.class); Worker workerToReturn = new Worker(); // expectations + Collection criterions = new ArrayList(); + expect( + criterionServiceMock + .getCriterionsFor(PredefinedCriterionTypes.LOCATION_GROUP)) + .andReturn(criterions).anyTimes(); expect(resourceServiceMock.findResource(workerToReturn.getId())) .andReturn(workerToReturn); - replay(resourceServiceMock); + replay(resourceServiceMock, criterionServiceMock); // perform actions + WorkerModel workerModel = new WorkerModel(resourceServiceMock, + criterionServiceMock); workerModel.prepareEditFor(workerToReturn); workerModel.save(); }