From d9f0a0ee51868b340eb97ad3a0c14824e7a7a98c Mon Sep 17 00:00:00 2001 From: Fernando Bellas Permuy Date: Fri, 8 Jan 2010 12:56:00 +0100 Subject: [PATCH] ItEr42S12CUImportacionRecursosProductivosItEr41S15: Support for importing resources with cost assignments. Support for importing resources with cost assignments has been implemented. Many test cases have been implemented for checking all constraint violations in cost assigments (special care has been taken to check time interval overlappings). toString method has been implemented in constraint violation related DTOs to facilitate debuging in testing. --- .../business/common/Registry.java | 10 +- .../costcategories/daos/CostCategoryDAO.java | 17 + .../costcategories/daos/ICostCategoryDAO.java | 4 + .../ResourcesCostCategoryAssignment.java | 62 ++- .../resources/daos/CriterionTypeDAO.java | 2 +- .../entities/CriterionSatisfaction.java | 4 +- .../resources/entities/CriterionType.java | 11 + .../business/resources/entities/Machine.java | 19 +- .../business/resources/entities/Resource.java | 48 ++- .../business/resources/entities/Worker.java | 15 +- .../business/orders/entities/Orders.hbm.xml | 5 +- .../ws/common/api/ConstraintViolationDTO.java | 27 +- .../api/InstanceConstraintViolationsDTO.java | 25 +- .../InstanceConstraintViolationsListDTO.java | 23 ++ .../ws/common/impl/DateConverter.java | 72 +--- .../api/CriterionSatisfactionDTO.java | 17 +- .../ws/resources/api/ResourceDTO.java | 6 + .../ResourcesCostCategoryAssignmentDTO.java | 55 +++ .../ws/resources/impl/ResourceConverter.java | 33 +- .../ws/resources/api/ResourceServiceTest.java | 369 +++++++++++++++--- scripts/rest-clients/README | 2 +- .../rest-clients/resources-sample-mini.xml | 52 +++ scripts/rest-clients/resources-sample.xml | 101 ++++- 23 files changed, 813 insertions(+), 166 deletions(-) create mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/ResourcesCostCategoryAssignmentDTO.java create mode 100644 scripts/rest-clients/resources-sample-mini.xml 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 ccffb5fef..0be1975d7 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 @@ -23,6 +23,7 @@ package org.navalplanner.business.common; import org.navalplanner.business.advance.daos.IAdvanceTypeDAO; import org.navalplanner.business.calendars.daos.IBaseCalendarDAO; import org.navalplanner.business.common.daos.IConfigurationDAO; +import org.navalplanner.business.costcategories.daos.ICostCategoryDAO; import org.navalplanner.business.costcategories.daos.ITypeOfWorkHoursDAO; import org.navalplanner.business.externalcompanies.daos.IExternalCompanyDAO; import org.navalplanner.business.labels.daos.ILabelDAO; @@ -107,7 +108,7 @@ public class Registry { @Autowired private IWorkerDAO workerDAO; - + @Autowired private IWorkReportLineDAO workReportLineDAO; @@ -120,6 +121,9 @@ public class Registry { @Autowired private IHoursGroupDAO hoursGroupDAO; + @Autowired + private ICostCategoryDAO costCategoryDAO; + private Registry() { } @@ -206,4 +210,8 @@ public class Registry { return getInstance().hoursGroupDAO; } + public static ICostCategoryDAO getCostCategoryDAO() { + return getInstance().costCategoryDAO; + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/CostCategoryDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/CostCategoryDAO.java index 3a9f6dc9d..b9ba22f10 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/CostCategoryDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/CostCategoryDAO.java @@ -28,6 +28,7 @@ import org.hibernate.Criteria; import org.hibernate.criterion.Restrictions; import org.joda.time.LocalDate; import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.costcategories.entities.CostCategory; import org.navalplanner.business.costcategories.entities.HourCost; import org.navalplanner.business.costcategories.entities.ResourcesCostCategoryAssignment; @@ -39,6 +40,7 @@ import org.springframework.transaction.annotation.Transactional; /** * @author Jacobo Aragunde Perez + * @author Fernando Bellas Permuy */ @Repository @Scope(BeanDefinition.SCOPE_SINGLETON) @@ -56,6 +58,21 @@ public class CostCategoryDAO extends GenericDAOHibernate return list; } + @Override + public CostCategory findUniqueByName(String name) + throws InstanceNotFoundException { + Criteria c = getSession().createCriteria(CostCategory.class). + add(Restrictions.eq("name", name).ignoreCase()); + CostCategory costCategory = (CostCategory) c.uniqueResult(); + + if (costCategory == null) { + throw new InstanceNotFoundException(name, + CostCategory.class.getName()); + } else { + return costCategory; + } + } + @Transactional(readOnly = true) public static BigDecimal getPriceByResourceDateAndHourType( Resource resource, diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/ICostCategoryDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/ICostCategoryDAO.java index aad493536..aae6e98d3 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/ICostCategoryDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/ICostCategoryDAO.java @@ -23,13 +23,17 @@ package org.navalplanner.business.costcategories.daos; import java.util.List; import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.costcategories.entities.CostCategory; /** * @author Jacobo Aragunde Perez + * @author Fernando Bellas Permuy */ public interface ICostCategoryDAO extends IGenericDAO { List findActive(); + CostCategory findUniqueByName(String name) throws InstanceNotFoundException; + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/ResourcesCostCategoryAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/ResourcesCostCategoryAssignment.java index 611ae0c3b..8f1765ea9 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/ResourcesCostCategoryAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/ResourcesCostCategoryAssignment.java @@ -20,26 +20,30 @@ package org.navalplanner.business.costcategories.entities; +import static org.navalplanner.business.i18n.I18nHelper._; + +import org.apache.commons.lang.StringUtils; import org.hibernate.validator.AssertTrue; import org.hibernate.validator.NotNull; import org.joda.time.LocalDate; import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.common.Registry; +import org.navalplanner.business.common.exceptions.CreateUnvalidatedException; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.resources.entities.Resource; /** * @author Jacobo Aragunde Perez + * @author Fernando Bellas Permuy */ public class ResourcesCostCategoryAssignment extends BaseEntity { - @NotNull private LocalDate initDate; private LocalDate endDate; - @NotNull private CostCategory costCategory; - @NotNull private Resource resource; // Default constructor, needed by Hibernate @@ -51,6 +55,39 @@ public class ResourcesCostCategoryAssignment extends BaseEntity { return (ResourcesCostCategoryAssignment) create(new ResourcesCostCategoryAssignment()); } + public static ResourcesCostCategoryAssignment createUnvalidated( + String costCategoryName, Resource resource, LocalDate initDate, + LocalDate endDate) throws CreateUnvalidatedException { + + /* Get CostCategory. */ + if (StringUtils.isBlank(costCategoryName)) { + throw new CreateUnvalidatedException( + _("cost category name not specified")); + } + + CostCategory costCategory = null; + try { + costCategory = Registry.getCostCategoryDAO().findUniqueByName( + StringUtils.trim(costCategoryName)); + } catch (InstanceNotFoundException e) { + throw new CreateUnvalidatedException( + _("{0}: cost category does not exist", costCategoryName)); + } + + /* Create instance of ResourcesCostCategoryAssignment. */ + ResourcesCostCategoryAssignment assignment = + create(new ResourcesCostCategoryAssignment()); + + assignment.initDate = initDate; + assignment.endDate = endDate; + assignment.costCategory = costCategory; + assignment.resource = resource; + + return assignment; + + } + + @NotNull(message="cost assignment's start date not specified") public LocalDate getInitDate() { return initDate; } @@ -67,6 +104,7 @@ public class ResourcesCostCategoryAssignment extends BaseEntity { this.endDate = endDate; } + @NotNull(message="cost assignment's category not specified") public CostCategory getCostCategory() { return costCategory; } @@ -75,6 +113,7 @@ public class ResourcesCostCategoryAssignment extends BaseEntity { this.costCategory = category; } + @NotNull(message="cost assignment's resource not specified") public Resource getResource() { return resource; } @@ -90,12 +129,25 @@ public class ResourcesCostCategoryAssignment extends BaseEntity { } } - @AssertTrue(message="The end date cannot be before the init date") - public boolean checkPositiveTimeInterval() { + @AssertTrue(message="cost assignment with end date less than start date") + public boolean checkConstraintPositiveTimeInterval() { + + /* Check if it makes sense to check the constraint .*/ + if (!isInitDateSpecified()) { + return true; + } + + /* Check the constraint. */ if (endDate == null) { return true; } + return (endDate.isAfter(initDate) || initDate.equals(endDate)); + + } + + public boolean isInitDateSpecified() { + return initDate != null; } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/CriterionTypeDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/CriterionTypeDAO.java index 04f1b61b6..1ad2b38ca 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/CriterionTypeDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/CriterionTypeDAO.java @@ -67,7 +67,7 @@ public class CriterionTypeDAO extends GenericDAOHibernate Criteria c = getSession().createCriteria(CriterionType.class); - c.add(Restrictions.eq("name", name)); + c.add(Restrictions.eq("name", name).ignoreCase()); CriterionType criterionType = (CriterionType) c.uniqueResult(); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionSatisfaction.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionSatisfaction.java index c4f500b9d..64344904a 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionSatisfaction.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionSatisfaction.java @@ -116,8 +116,8 @@ public class CriterionSatisfaction extends BaseEntity { /* Create instance of CriterionSatisfaction. */ CriterionSatisfaction criterionSatisfaction = - new CriterionSatisfaction(); - criterionSatisfaction.setNewObject(true); + create(new CriterionSatisfaction()); + criterionSatisfaction.criterion = criterion; criterionSatisfaction.resource = resource; criterionSatisfaction.startDate = startDate; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java index 3c649b7fa..0f865126c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java @@ -23,6 +23,7 @@ package org.navalplanner.business.resources.entities; import java.util.HashSet; import java.util.Set; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.hibernate.validator.AssertTrue; import org.hibernate.validator.NotEmpty; @@ -318,6 +319,12 @@ public class CriterionType extends BaseEntity implements @AssertTrue(message="criterion type name is already being used") public boolean checkConstraintUniqueCriterionTypeName() { + /* Check if it makes sense to check the constraint .*/ + if (!isNameSpecified()) { + return true; + } + + /* Check the constraint. */ ICriterionTypeDAO criterionTypeDAO = Registry.getCriterionTypeDAO(); if (isNewObject()) { @@ -365,4 +372,8 @@ public class CriterionType extends BaseEntity implements } + private boolean isNameSpecified() { + return !StringUtils.isBlank(name); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Machine.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Machine.java index 62a222954..8f4d7d09a 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Machine.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Machine.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import org.apache.commons.lang.StringUtils; import org.hibernate.validator.AssertTrue; import org.hibernate.validator.NotEmpty; import org.hibernate.validator.Valid; @@ -43,14 +44,14 @@ public class Machine extends Resource { configurationUnits.remove(unit); } - public static Machine createUnvalidated(String code, String name, String description) { + public static Machine createUnvalidated(String code, String name, + String description) { - Machine machine = new Machine(); + Machine machine = create(new Machine()); machine.code = code; machine.name = name; machine.description = description; - machine.setNewObject(true); return machine; @@ -108,6 +109,13 @@ public class Machine extends Resource { @AssertTrue(message="machine code has to be unique. It is already used") public boolean checkConstraintUniqueCode() { + + /* Check if it makes sense to check the constraint .*/ + if (!isCodeSpecified()) { + return true; + } + + /* Check the constraint. */ boolean result; if (isNewObject()) { result = !existsMachineWithTheCode(); @@ -115,6 +123,11 @@ public class Machine extends Resource { result = isIfExistsTheExistentMachineThisOne(); } return result; + + } + + private boolean isCodeSpecified() { + return !StringUtils.isBlank(code); } private boolean existsMachineWithTheCode() { 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 0e18ce012..4d5b3cd23 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 @@ -51,14 +51,6 @@ import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.costcategories.entities.ResourcesCostCategoryAssignment; import org.navalplanner.business.planner.entities.DayAssignment; -// FIXME: Alternatively, Resource can be modeled with the style: -// Resource.getParent() & Resource.getChilds(). This way, Resource does not -// depend on ResourceGroup. However, such an option allows combinations not -// semantically correct (e.g. a simple resource, such as Worker, could be the -// child another simple resource, general methods like getChilds() do not make -// sense for simple entities, etc.). In consequence, I prefer the modeling -// option shown below. - /** * This class acts as the base class for all resources. * @author Fernando Bellas Permuy @@ -822,8 +814,26 @@ public abstract class Resource extends BaseEntity{ assignment.setResource(null); } - @AssertFalse(message="Two assignments overlap in time") - public boolean checkAssignmentsOverlap() { + @AssertFalse(message="Some cost category assignments overlap in time") + public boolean checkConstraintAssignmentsOverlap() { + + /* + * Check if time intervals in cost assignments are correct in isolation. + * If not, it does not make sense to check assignment overlapping. + */ + for (ResourcesCostCategoryAssignment i : + getResourcesCostCategoryAssignments()) { + + if (!(i.isInitDateSpecified() && + i.checkConstraintPositiveTimeInterval())) { + return false; + } + + } + + /* + * Check assignment overlapping. + */ List assignmentsList = new ArrayList(); assignmentsList.addAll(getResourcesCostCategoryAssignments()); @@ -832,11 +842,6 @@ public abstract class Resource extends BaseEntity{ LocalDate endDate = assignmentsList.get(i).getEndDate(); for(int j=i+1; j=0 && - listElement.getEndDate().compareTo(endDate)<=0) || - (listElement.getInitDate().compareTo(initDate)>=0 && - listElement.getInitDate().compareTo(endDate)<=0))) { + ((listElement.getEndDate().compareTo(initDate)>=0 && // (1) listElement.getEndDate() inside [initDate, endDate] + listElement.getEndDate().compareTo(endDate)<=0) || + (listElement.getInitDate().compareTo(initDate)>=0 && // (2) listElement.getInitDate() inside [initDate, endDate] + listElement.getInitDate().compareTo(endDate)<=0) || + (listElement.getInitDate().compareTo(initDate)<=0 && // (3) [listElement.getInitDate(), listElement.getEndDate()] + listElement.getEndDate().compareTo(endDate)>=0))) { // contains [initDate, endDate] return true; } } } return false; + } public boolean isVirtual() { return false; } - @AssertTrue(message="there are criterion satisfactions referring to " + + @AssertTrue(message="there exist criterion satisfactions referring to " + "criterion types not applicable to this resource") public boolean checkConstraintCriterionSatisfactionsWithCorrectType() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java index 02958898c..aca6bd991 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java @@ -50,12 +50,11 @@ public class Worker extends Resource { public static Worker createUnvalidated(String firstName, String surname, String nif) { - Worker worker = new Worker(); + Worker worker = create(new Worker()); worker.firstName = firstName; worker.surname = surname; worker.nif = nif; - worker.setNewObject(true); return worker; @@ -131,14 +130,16 @@ public class Worker extends Resource { @AssertTrue(message = "Worker with the same first name, surname and nif previously existed") public boolean checkConstraintUniqueFirstName() { - if (!firstLevelValidationsPassed()) { - return true; - } - + /* Check if it makes sense to check the constraint .*/ if (this instanceof VirtualWorker) { return true; } + if (!areFirstNameSurnameNifSpecified()) { + return true; + } + + /* Check the constraint. */ List list = Registry.getWorkerDAO() .findByFirstNameSecondNameAndNifAnotherTransaction(firstName, surname, nif); @@ -155,7 +156,7 @@ public class Worker extends Resource { } - private boolean firstLevelValidationsPassed() { + private boolean areFirstNameSurnameNifSpecified() { return !StringUtils.isBlank(firstName) && !StringUtils.isBlank(surname) && diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml index 77c0bd054..1ca8c46fb 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml @@ -49,7 +49,7 @@ - + @@ -113,7 +113,8 @@ + not-null="true" + class="org.navalplanner.business.orders.entities.OrderLine"/> diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/ConstraintViolationDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/ConstraintViolationDTO.java index 2be59df5f..95c027ca1 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/ConstraintViolationDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/ConstraintViolationDTO.java @@ -20,6 +20,9 @@ package org.navalplanner.ws.common.api; +import java.io.PrintWriter; +import java.io.StringWriter; + import javax.xml.bind.annotation.XmlAttribute; /** @@ -29,10 +32,13 @@ import javax.xml.bind.annotation.XmlAttribute; */ public class ConstraintViolationDTO { - @XmlAttribute(name="field-name") + public final static String FIELD_NAME_ATTRIBUTE_NAME = "field-name"; + public final static String MESSAGE_ATTRIBUTE_NAME = "message"; + + @XmlAttribute(name=FIELD_NAME_ATTRIBUTE_NAME) public String fieldName; - @XmlAttribute + @XmlAttribute(name=MESSAGE_ATTRIBUTE_NAME) public String message; public ConstraintViolationDTO() {} @@ -42,4 +48,21 @@ public class ConstraintViolationDTO { this.message = message; } + @Override + public String toString() { + + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + + if (fieldName != null) { + printWriter.print(FIELD_NAME_ATTRIBUTE_NAME + " = " + fieldName + + " - "); + } + printWriter.println(MESSAGE_ATTRIBUTE_NAME + " = " + message); + printWriter.close(); + + return stringWriter.toString(); + + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsDTO.java index 1ea323ea5..728497806 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsDTO.java @@ -20,6 +20,8 @@ package org.navalplanner.ws.common.api; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.List; @@ -33,7 +35,9 @@ import javax.xml.bind.annotation.XmlElement; */ public class InstanceConstraintViolationsDTO { - @XmlAttribute(name="instance-id") + public final static String INSTANCE_ID_ATTRIBUTE_NAME = "instance-id"; + + @XmlAttribute(name=INSTANCE_ID_ATTRIBUTE_NAME) public String instanceId; @XmlElement(name="constraint-violation") @@ -62,4 +66,23 @@ public class InstanceConstraintViolationsDTO { } + @Override + public String toString() { + + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + + printWriter.println("** " + INSTANCE_ID_ATTRIBUTE_NAME + " = " + + instanceId + " **"); + + for (ConstraintViolationDTO i : constraintViolations) { + printWriter.println(i); + } + + printWriter.close(); + + return stringWriter.toString(); + + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsListDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsListDTO.java index 73ac3d837..c712714a5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsListDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsListDTO.java @@ -20,6 +20,8 @@ package org.navalplanner.ws.common.api; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.List; import javax.xml.bind.annotation.XmlElement; @@ -48,4 +50,25 @@ public class InstanceConstraintViolationsListDTO { } + @Override + public String toString() { + + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + + printWriter.println("*** " + this.getClass().getName() + " ***"); + + for (InstanceConstraintViolationsDTO i : + instanceConstraintViolationsList) { + + printWriter.println(i); + + } + + printWriter.close(); + + return stringWriter.toString(); + + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/DateConverter.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/DateConverter.java index 7ab1299e1..627368fb9 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/DateConverter.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/DateConverter.java @@ -20,14 +20,12 @@ package org.navalplanner.ws.common.impl; -import java.util.Calendar; import java.util.Date; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeConstants; -import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; +import org.joda.time.LocalDate; + /** * A converter from java.util.Date to/from * javax.xml.datatype.XMLGregorianCalendar. @@ -38,53 +36,6 @@ public class DateConverter { private DateConverter() {} - /** - * It converts a Date to a XMLGregorianCalendar - * representing a xsd:date XML type.

- * - * If the date passed as a parameter is null, it also returns - * null. - */ - public final static XMLGregorianCalendar toXMLGregrorianCalendar( - Date date) { - - if (date == null) { - return null; - } - - Calendar dateAsCalendar = Calendar.getInstance(); - dateAsCalendar.setTime(date); - XMLGregorianCalendar dateAsXMLGregorianCalendar = null; - - try { - dateAsXMLGregorianCalendar = - DatatypeFactory.newInstance().newXMLGregorianCalendarDate( - dateAsCalendar.get(Calendar.YEAR), - convertMonthFieldFromCalendarToXMLGregorianCalendar( - dateAsCalendar.get(Calendar.MONTH)), - dateAsCalendar.get(Calendar.DAY_OF_MONTH), - DatatypeConstants.FIELD_UNDEFINED); - } catch (DatatypeConfigurationException e) { - throw new RuntimeException(e); - } - - return dateAsXMLGregorianCalendar; - - } - - /** - * Converts from @{link Calendar} month field format to - * @{link XMLGregorianCalendar} format. - * - * It is needed the conversion because - * @{link XMLGregorianCalendar} months go from 1 to 12 while - * @{link Calendar} months go from 0 to 11 - * - */ - private final static int convertMonthFieldFromCalendarToXMLGregorianCalendar(int month) { - return month+1; - } - /** * It converts a XMLGregorianCalendar representing a * xsd:date XML type to a Date.

@@ -102,4 +53,23 @@ public class DateConverter { } + /** + * It converts a XMLGregorianCalendar representing a + * xsd:date XML type to a Joda's LocalDate. + *

+ * + * If the date passed as a parameter is null, it also returns + * null. + */ + public final static LocalDate toLocalDate(XMLGregorianCalendar date) { + + if (date == null) { + return null; + } else { + return new LocalDate(date.getYear(), date.getMonth(), + date.getDay()); + } + + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/CriterionSatisfactionDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/CriterionSatisfactionDTO.java index 1ae9b064f..327f47eff 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/CriterionSatisfactionDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/CriterionSatisfactionDTO.java @@ -20,14 +20,10 @@ package org.navalplanner.ws.resources.api; -import java.util.Date; - import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlSchemaType; import javax.xml.datatype.XMLGregorianCalendar; -import org.navalplanner.ws.common.impl.DateConverter; - /** * DTO for CriterionSatisfaction entity. * @@ -45,19 +41,20 @@ public class CriterionSatisfactionDTO { @XmlSchemaType(name="date") public XMLGregorianCalendar startDate; - @XmlAttribute(name="finish-date") + @XmlAttribute(name="end-date") @XmlSchemaType(name="date") - public XMLGregorianCalendar finishDate; + public XMLGregorianCalendar endDate; public CriterionSatisfactionDTO() {} - public CriterionSatisfactionDTO(String criterionTypeName, - String criterionName, Date startDate, Date finishDate) { + public CriterionSatisfactionDTO( + String criterionTypeName, String criterionName, + XMLGregorianCalendar startDate, XMLGregorianCalendar endDate) { this.criterionTypeName = criterionTypeName; this.criterionName = criterionName; - this.startDate = DateConverter.toXMLGregrorianCalendar(startDate); - this.finishDate = DateConverter.toXMLGregrorianCalendar(finishDate); + this.startDate = startDate; + this.endDate = endDate; } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/ResourceDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/ResourceDTO.java index c2fba8c14..35f87180b 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/ResourceDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/ResourceDTO.java @@ -42,4 +42,10 @@ public abstract class ResourceDTO { public List criterionSatisfactions = new ArrayList(); + @XmlElementWrapper(name="resources-cost-category-assignment-list") + @XmlElement(name="resources-cost-category-assignment") + public List + resourcesCostCategoryAssignments = + new ArrayList(); + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/ResourcesCostCategoryAssignmentDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/ResourcesCostCategoryAssignmentDTO.java new file mode 100644 index 000000000..dc3e063bd --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/api/ResourcesCostCategoryAssignmentDTO.java @@ -0,0 +1,55 @@ +/* + * 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.ws.resources.api; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.datatype.XMLGregorianCalendar; + +/** + * DTO for ResourcesCostCategoryAssignment entity. + * + * @author Fernando Bellas Permuy + */ +public class ResourcesCostCategoryAssignmentDTO { + + @XmlAttribute(name="cost-category-name") + public String costCategoryName; + + @XmlAttribute(name="start-date") + @XmlSchemaType(name="date") + public XMLGregorianCalendar startDate; + + @XmlAttribute(name="end-date") + @XmlSchemaType(name="date") + public XMLGregorianCalendar endDate; + + public ResourcesCostCategoryAssignmentDTO() {} + + public ResourcesCostCategoryAssignmentDTO(String costCategoryName, + XMLGregorianCalendar startDate, XMLGregorianCalendar endDate) { + + this.costCategoryName = costCategoryName; + this.startDate = startDate; + this.endDate = endDate; + + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/impl/ResourceConverter.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/impl/ResourceConverter.java index b0c54e74e..e6fed52fb 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/impl/ResourceConverter.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/impl/ResourceConverter.java @@ -28,6 +28,7 @@ import org.apache.commons.lang.StringUtils; import org.navalplanner.business.common.exceptions.CreateUnvalidatedException; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.common.exceptions.MultipleInstancesException; +import org.navalplanner.business.costcategories.entities.ResourcesCostCategoryAssignment; import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.Machine; import org.navalplanner.business.resources.entities.Resource; @@ -36,6 +37,7 @@ import org.navalplanner.ws.common.impl.DateConverter; import org.navalplanner.ws.resources.api.CriterionSatisfactionDTO; import org.navalplanner.ws.resources.api.MachineDTO; import org.navalplanner.ws.resources.api.ResourceDTO; +import org.navalplanner.ws.resources.api.ResourcesCostCategoryAssignmentDTO; import org.navalplanner.ws.resources.api.WorkerDTO; /** @@ -65,6 +67,8 @@ public class ResourceConverter { addCriterionSatisfactions(resource, resourceDTO.criterionSatisfactions); setResourceCalendar(resource, resourceDTO.calendarName); + addResourcesCostCategoryAssignments(resource, + resourceDTO.resourcesCostCategoryAssignments); return resource; @@ -109,7 +113,7 @@ public class ResourceConverter { StringUtils.trim(criterionSatisfactionDTO.criterionName), resource, DateConverter.toDate(criterionSatisfactionDTO.startDate), - DateConverter.toDate(criterionSatisfactionDTO.finishDate)); + DateConverter.toDate(criterionSatisfactionDTO.endDate)); } @@ -129,4 +133,31 @@ public class ResourceConverter { } + private static void addResourcesCostCategoryAssignments( + Resource resource, List + resourcesCostCategoryAssignments) + throws CreateUnvalidatedException { + + for (ResourcesCostCategoryAssignmentDTO assignmentDTO : + resourcesCostCategoryAssignments) { + + ResourcesCostCategoryAssignment assignment = toEntity(assignmentDTO, + resource); + resource.addResourcesCostCategoryAssignment(assignment); + + } + + } + + private static ResourcesCostCategoryAssignment toEntity( + ResourcesCostCategoryAssignmentDTO assignmentDTO, Resource resource) + throws CreateUnvalidatedException { + + return ResourcesCostCategoryAssignment.createUnvalidated( + assignmentDTO.costCategoryName, resource, + DateConverter.toLocalDate(assignmentDTO.startDate), + DateConverter.toLocalDate(assignmentDTO.endDate)); + + } + } diff --git a/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/resources/api/ResourceServiceTest.java b/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/resources/api/ResourceServiceTest.java index 99ef7a5d3..62384f574 100644 --- a/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/resources/api/ResourceServiceTest.java +++ b/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/resources/api/ResourceServiceTest.java @@ -29,10 +29,14 @@ import static org.navalplanner.web.WebappGlobalNames.WEBAPP_SPRING_CONFIG_FILE; import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; import java.util.UUID; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeConstants; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,6 +47,8 @@ import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.common.daos.IConfigurationDAO; import org.navalplanner.business.common.entities.IConfigurationBootstrap; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.costcategories.daos.ICostCategoryDAO; +import org.navalplanner.business.costcategories.entities.CostCategory; import org.navalplanner.business.resources.daos.ICriterionTypeDAO; import org.navalplanner.business.resources.daos.IMachineDAO; import org.navalplanner.business.resources.daos.IResourceDAO; @@ -61,6 +67,7 @@ import org.navalplanner.ws.resources.api.IResourceService; import org.navalplanner.ws.resources.api.MachineDTO; import org.navalplanner.ws.resources.api.ResourceDTO; import org.navalplanner.ws.resources.api.ResourceListDTO; +import org.navalplanner.ws.resources.api.ResourcesCostCategoryAssignmentDTO; import org.navalplanner.ws.resources.api.WorkerDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.NotTransactional; @@ -100,6 +107,9 @@ public class ResourceServiceTest { @Autowired private IBaseCalendarDAO baseCalendarDAO; + @Autowired + private ICostCategoryDAO costCategoryDAO; + @Autowired private IConfigurationBootstrap configurationBootstrap; @@ -135,10 +145,18 @@ public class ResourceServiceTest { resourceService.addResources(createResourceListDTO(m1, m2, w1, w2)). instanceConstraintViolationsList; - assertTrue(instanceConstraintViolationsList.size() == 2); - assertTrue(instanceConstraintViolationsList.get(0). + assertTrue( + instanceConstraintViolationsList.toString(), + instanceConstraintViolationsList.size() == 2); + assertTrue( + instanceConstraintViolationsList.get(0). + constraintViolations.toString(), + instanceConstraintViolationsList.get(0). constraintViolations.size() == 2); // m2 constraint violations. - assertTrue(instanceConstraintViolationsList.get(1). + assertTrue( + instanceConstraintViolationsList.get(1). + constraintViolations.toString(), + instanceConstraintViolationsList.get(1). constraintViolations.size() == 3); // w2 constraint violations. machineDAO.findUniqueByCode(m1Code.trim()); assertTrue( @@ -208,10 +226,18 @@ public class ResourceServiceTest { resourceService.addResources(createResourceListDTO(m1, m2, w1, w2)). instanceConstraintViolationsList; - assertTrue(instanceConstraintViolationsList.size() == 2); - assertTrue(instanceConstraintViolationsList.get(0). + assertTrue( + instanceConstraintViolationsList.toString(), + instanceConstraintViolationsList.size() == 2); + assertTrue( + instanceConstraintViolationsList.get(0). + constraintViolations.toString(), + instanceConstraintViolationsList.get(0). constraintViolations.size() == 1); - assertTrue(instanceConstraintViolationsList.get(1). + assertTrue( + instanceConstraintViolationsList.get(1). + constraintViolations.toString(), + instanceConstraintViolationsList.get(1). constraintViolations.size() == 1); machineDAO.findUniqueByCode(m1.code); assertTrue( @@ -232,19 +258,22 @@ public class ResourceServiceTest { MachineDTO machineDTO = new MachineDTO(getUniqueName(), "name", "desc"); machineDTO.criterionSatisfactions.add( new CriterionSatisfactionDTO( - ' ' + ct.getName() + ' ', " c1 ", // Blank spaces intentionally - // added (OK). - Calendar.getInstance().getTime(), null)); + ' ' + ct.getName().toUpperCase() + // Upper case and blank + ' ', " C1 ", // spaces intentionally + // added (OK). + getDate(2001, 1, 1), null)); machineDTO.criterionSatisfactions.add( new CriterionSatisfactionDTO(ct.getName(), "c2", - Calendar.getInstance().getTime(), null)); + getDate(2001, 1, 1), null)); /* Test. */ List instanceConstraintViolationsList = resourceService.addResources(createResourceListDTO(machineDTO)). instanceConstraintViolationsList; - assertTrue(instanceConstraintViolationsList.isEmpty()); + assertTrue( + instanceConstraintViolationsList.toString(), + instanceConstraintViolationsList.isEmpty()); Machine machine = findUniqueMachineByCodeInitialized(machineDTO.code); assertTrue(machine.getCriterionSatisfactions().size() == 2); @@ -269,7 +298,7 @@ public class ResourceServiceTest { MachineDTO machineDTO = new MachineDTO(getUniqueName(), "name", "desc"); machineDTO.criterionSatisfactions.add( new CriterionSatisfactionDTO(ct.getName() , "c1", - null, Calendar.getInstance().getTime())); // Missing start date. + null, getDate(2001, 1, 1))); // Missing start date. /* Test. */ assertOneConstraintViolation( @@ -281,7 +310,7 @@ public class ResourceServiceTest { @Test @NotTransactional - public void testAddResourceWithCriterionSatisfactionsWithIncorrectType() { + public void testAddResourcesWithCriterionSatisfactionsWithIncorrectType() { /* Create two criterion types. */ CriterionType machineCt = createCriterionType(ResourceEnum.MACHINE); @@ -291,11 +320,11 @@ public class ResourceServiceTest { MachineDTO machineDTO = new MachineDTO(getUniqueName(), "name", "desc"); machineDTO.criterionSatisfactions.add( new CriterionSatisfactionDTO(workerCt.getName() , "c1", - Calendar.getInstance().getTime(), null)); // Incorrect type. + getDate(2001, 1, 1), null)); // Incorrect type. WorkerDTO workerDTO = new WorkerDTO(getUniqueName(), "surname", "nif"); workerDTO.criterionSatisfactions.add( new CriterionSatisfactionDTO(machineCt.getName() , "c1", - Calendar.getInstance().getTime(), null)); // Incorrect type. + getDate(2001, 1, 1), null)); // Incorrect type. /* Test. */ assertOneConstraintViolation( @@ -311,53 +340,45 @@ public class ResourceServiceTest { @Test @NotTransactional - public void testAddResourceWithCriterionSatisfactionsWithIncorrectNames() { + public void testAddResourcesWithCriterionSatisfactionsWithIncorrectNames() { /* Create a criterion type. */ CriterionType ct = createCriterionType(); /* Create machines DTOs. */ - MachineDTO m1 = new MachineDTO(getUniqueName(), "name", "desc"); + MachineDTO m1 = new MachineDTO(getUniqueName(), "m1", "desc"); m1.criterionSatisfactions.add( new CriterionSatisfactionDTO("", "X", // Missing criterion type. - Calendar.getInstance().getTime(), null)); - MachineDTO m2 = new MachineDTO(getUniqueName(), "name", "desc"); + getDate(2001, 1, 1), null)); + MachineDTO m2 = new MachineDTO(getUniqueName(), "m2", "desc"); m2.criterionSatisfactions.add( new CriterionSatisfactionDTO(ct.getName(), // Missing criterion. - null, Calendar.getInstance().getTime(), null)); - MachineDTO m3 = new MachineDTO(getUniqueName(), "name", "desc"); + null, getDate(2001, 1, 1), null)); + MachineDTO m3 = new MachineDTO(getUniqueName(), "m3", "desc"); m3.criterionSatisfactions.add( new CriterionSatisfactionDTO( ct.getName() + 'X', // Non-existent criterion type. - "c1", Calendar.getInstance().getTime(), null)); - MachineDTO m4 = new MachineDTO(getUniqueName(), "name", "desc"); + "c1", getDate(2001, 1, 1), null)); + MachineDTO m4 = new MachineDTO(getUniqueName(), "m4", "desc"); m4.criterionSatisfactions.add( new CriterionSatisfactionDTO( ct.getName(), "c1" + 'X', // Criterion name is not of ct's type. - Calendar.getInstance().getTime(), null)); + getDate(2001, 1, 1), null)); /* Test. */ - List machines = new ArrayList(); - machines.add(m1); - machines.add(m2); - machines.add(m3); - machines.add(m4); + ResourceListDTO resourceDTOs = createResourceListDTO(m1, m2, m3, m4); - List instanceConstraintViolationsList = - resourceService.addResources(new ResourceListDTO(machines)). - instanceConstraintViolationsList; + assertOneConstraintViolationPerInstance( + resourceService.addResources(resourceDTOs), + resourceDTOs.resources.size()); - assertTrue(instanceConstraintViolationsList.size() == machines.size()); - - for (InstanceConstraintViolationsDTO i : - instanceConstraintViolationsList) { - assertTrue(i.constraintViolations.size() == 1); - } - - for (MachineDTO m : machines) { + for (ResourceDTO r : resourceDTOs.resources) { + MachineDTO m = (MachineDTO) r; assertFalse( - machineDAO.existsMachineWithCodeInAnotherTransaction(m.code)); + "Machine " + m.name + " not expected", + machineDAO.existsMachineWithCodeInAnotherTransaction( + ((MachineDTO) r).code)); } } @@ -418,6 +439,183 @@ public class ResourceServiceTest { } + @Test + @NotTransactional + public void testAddResourceWithCostAssignments() { + + /* Create a CostCategory. */ + CostCategory costCategory = createCostCategory(); + + /* Create resource DTOs. */ + MachineDTO machineDTO = new MachineDTO(getUniqueName(), "name", "desc"); + machineDTO.resourcesCostCategoryAssignments.add( + new ResourcesCostCategoryAssignmentDTO( + ' ' + costCategory.getName().toUpperCase() + ' ', + getDate(2001, 1, 1), null)); + machineDTO.resourcesCostCategoryAssignments.add( + new ResourcesCostCategoryAssignmentDTO( + costCategory.getName(), + getDate(2000, 1, 1), getDate(2000, 4, 1))); + + /* Test. */ + assertNoConstraintViolations( + resourceService.addResources(createResourceListDTO(machineDTO))); + assertTrue(machineDAO.existsMachineWithCodeInAnotherTransaction( + machineDTO.code)); + + } + + @Test + @NotTransactional + public void testAddResourcesWithCostAssignmentWithIncorrectCategoryNames() { + + /* Create a resource DTOs. */ + MachineDTO m1 = new MachineDTO(getUniqueName(), "m1", "desc"); + m1.resourcesCostCategoryAssignments.add( + new ResourcesCostCategoryAssignmentDTO( + null, // Cost category not specified. + getDate(2000, 1, 1), null)); + + MachineDTO m2 = new MachineDTO(getUniqueName(), "m2", "desc"); + m2.resourcesCostCategoryAssignments.add( + new ResourcesCostCategoryAssignmentDTO( + getUniqueName(), // Non-existent cost category. + getDate(2000, 1, 1), null)); + + /* Test. */ + ResourceListDTO resourceDTOs = createResourceListDTO(m1, m2); + + assertOneConstraintViolationPerInstance( + resourceService.addResources(resourceDTOs), + resourceDTOs.resources.size()); + + for (ResourceDTO r : resourceDTOs.resources) { + MachineDTO m = (MachineDTO) r; + assertFalse( + "Machine " + m.name + " not expected", + machineDAO.existsMachineWithCodeInAnotherTransaction(m.code)); + }; + + } + + @Test + @NotTransactional + public void testAddResourceWithCostAssignmentWithoutStartDate() { + + /* Create a CostCategory. */ + CostCategory costCategory = createCostCategory(); + + /* Create a resource DTO. */ + MachineDTO machineDTO = new MachineDTO(getUniqueName(), "name", "desc"); + machineDTO.resourcesCostCategoryAssignments.add( + new ResourcesCostCategoryAssignmentDTO( + costCategory.getName(), null, // Start date not specified. + getDate(2000, 1, 1))); + + /* Test. */ + assertOneConstraintViolation( + resourceService.addResources(createResourceListDTO(machineDTO))); + assertFalse(machineDAO.existsMachineWithCodeInAnotherTransaction( + machineDTO.code)); + + } + + @Test + @NotTransactional + public void testAddResourceWithCostAssignmentWithNegativeInterval() { + + /* Create a CostCategory. */ + CostCategory costCategory = createCostCategory(); + + /* Create a resource DTO. */ + MachineDTO machineDTO = new MachineDTO(getUniqueName(), "name", "desc"); + machineDTO.resourcesCostCategoryAssignments.add( + new ResourcesCostCategoryAssignmentDTO( + costCategory.getName(), + getDate(2000, 2, 1), getDate(2000, 1, 1))); + + /* Test. */ + assertOneConstraintViolation( + resourceService.addResources(createResourceListDTO(machineDTO))); + assertFalse(machineDAO.existsMachineWithCodeInAnotherTransaction( + machineDTO.code)); + + } + + @Test + @NotTransactional + public void testAddResourcesWithOverlappingInCostAssignments() { + + /* Create a CostCategory. */ + CostCategory costCategory = createCostCategory(); + + /* + * Create a resource DTOs. Each resource contains one cost assignment + * overlapping. + */ + MachineDTO m1 = createMachineDTOWithTwoCostsAssignments( + "m1", costCategory.getName(), + getDate(2000, 1, 1), null, + getDate(2000, 2, 1), null); + + MachineDTO m2 = createMachineDTOWithTwoCostsAssignments( + "m2", costCategory.getName(), + getDate(2000, 2, 1), null, + getDate(2000, 1, 1), getDate(2000, 3, 1)); + + MachineDTO m3 = createMachineDTOWithTwoCostsAssignments( + "m3", costCategory.getName(), + getDate(2000, 2, 1), getDate(2000, 4, 1), + getDate(2000, 3, 1), null); + + MachineDTO m4 = createMachineDTOWithTwoCostsAssignments( + "m4", costCategory.getName(), + getDate(2000, 2, 1), getDate(2000, 5, 1), + getDate(2000, 1, 1), getDate(2000, 3, 1)); + + MachineDTO m5 = createMachineDTOWithTwoCostsAssignments( + "m5", costCategory.getName(), + getDate(2000, 2, 1), getDate(2000, 5, 1), + getDate(2000, 3, 1), getDate(2000, 4, 1)); + + MachineDTO m6 = createMachineDTOWithTwoCostsAssignments( + "m6", costCategory.getName(), + getDate(2000, 2, 1), getDate(2000, 5, 1), + getDate(2000, 4, 1), getDate(2000, 6, 1)); + + MachineDTO m7 = createMachineDTOWithTwoCostsAssignments( + "m7", costCategory.getName(), + getDate(2000, 2, 1), getDate(2000, 5, 1), + getDate(2000, 1, 1), getDate(2000, 2, 1)); + + MachineDTO m8 = createMachineDTOWithTwoCostsAssignments( + "m8", costCategory.getName(), + getDate(2000, 2, 1), getDate(2000, 5, 1), + getDate(2000, 5, 1), getDate(2000, 6, 1)); + + MachineDTO m9 = createMachineDTOWithTwoCostsAssignments( + "m9", costCategory.getName(), + getDate(2000, 2, 1), getDate(2000, 5, 1), + getDate(2000, 2, 1), getDate(2000, 5, 1)); + + /* Test. */ + ResourceListDTO resourceDTOs = createResourceListDTO( + m1, m2, m3, m4, m5, m6, m7, m8, m9); + + assertOneConstraintViolationPerInstance( + resourceService.addResources(resourceDTOs), + resourceDTOs.resources.size()); + + for (ResourceDTO r : resourceDTOs.resources) { + MachineDTO m = (MachineDTO) r; + assertFalse( + "Machine " + m.name + " not expected", + machineDAO.existsMachineWithCodeInAnotherTransaction( + ((MachineDTO) r).code)); + } + + } + private CriterionType createCriterionType() { return createCriterionType(ResourceEnum.RESOURCE); } @@ -501,7 +699,7 @@ public class ResourceServiceTest { } - public BaseCalendar getDefaultCalendar() { + private BaseCalendar getDefaultCalendar() { IOnTransaction find = new IOnTransaction() { @@ -546,11 +744,51 @@ public class ResourceServiceTest { } + private CostCategory createCostCategory() { + + IOnTransaction create = + new IOnTransaction() { + + @Override + public CostCategory execute() { + CostCategory costCategory = + CostCategory.create(getUniqueName()); + costCategoryDAO.save(costCategory); + return costCategory; + } + }; + + return transactionService.runOnTransaction(create); + + } + + private MachineDTO createMachineDTOWithTwoCostsAssignments( + String machineName, String costCategoryName, + XMLGregorianCalendar startDate1, XMLGregorianCalendar endDate1, + XMLGregorianCalendar startDate2, XMLGregorianCalendar endDate2) { + + MachineDTO machineDTO = new MachineDTO(getUniqueName(), machineName, + "desc"); + + machineDTO.resourcesCostCategoryAssignments.add( + new ResourcesCostCategoryAssignmentDTO( + costCategoryName, startDate1, endDate1)); + machineDTO.resourcesCostCategoryAssignments.add( + new ResourcesCostCategoryAssignmentDTO( + costCategoryName, startDate2, endDate2)); + + return machineDTO; + + } + private void assertNoConstraintViolations( InstanceConstraintViolationsListDTO instanceConstraintViolationsListDTO) { - assertTrue(instanceConstraintViolationsListDTO. + assertTrue( + instanceConstraintViolationsListDTO. + instanceConstraintViolationsList.toString(), + instanceConstraintViolationsListDTO. instanceConstraintViolationsList.size() == 0); } @@ -563,9 +801,46 @@ public class ResourceServiceTest { instanceConstraintViolationsListDTO. instanceConstraintViolationsList; - assertTrue(instanceConstraintViolationsList.size() == 1); - assertTrue(instanceConstraintViolationsList.get(0). - constraintViolations.size() == 1); + assertTrue( + instanceConstraintViolationsList.toString(), + instanceConstraintViolationsList.size() == 1); + assertTrue( + instanceConstraintViolationsList.get(0). + constraintViolations.toString(), + instanceConstraintViolationsList.get(0). + constraintViolations.size() == 1); + + } + + private void assertOneConstraintViolationPerInstance( + InstanceConstraintViolationsListDTO + instanceConstraintViolationsListDTO, int numberOfInstances) { + + List instanceConstraintViolationsList = + instanceConstraintViolationsListDTO. + instanceConstraintViolationsList; + + assertTrue( + instanceConstraintViolationsList.toString(), + instanceConstraintViolationsList.size() == numberOfInstances); + + for (InstanceConstraintViolationsDTO i : + instanceConstraintViolationsList) { + assertTrue( + i.constraintViolations.toString(), + i.constraintViolations.size() == 1); + } + + } + + private XMLGregorianCalendar getDate(int year, int month, int day) { + + try { + return DatatypeFactory.newInstance().newXMLGregorianCalendarDate( + year, month, day, DatatypeConstants.FIELD_UNDEFINED); + } catch (DatatypeConfigurationException e) { + throw new RuntimeException(e); + } } diff --git a/scripts/rest-clients/README b/scripts/rest-clients/README index 394b15daa..64adc0dd4 100644 --- a/scripts/rest-clients/README +++ b/scripts/rest-clients/README @@ -22,7 +22,7 @@ * Import resources: - - import-resources.sh resources-sample.xml + - import-resources.sh resources-sample-mini.xml (or resources-sample.xml) (authenticate with wswriter/wswriter) - Check the returned errors are consistent with the comments in diff --git a/scripts/rest-clients/resources-sample-mini.xml b/scripts/rest-clients/resources-sample-mini.xml new file mode 100644 index 000000000..a07fd00d4 --- /dev/null +++ b/scripts/rest-clients/resources-sample-mini.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/rest-clients/resources-sample.xml b/scripts/rest-clients/resources-sample.xml index 11646379f..dba584c89 100644 --- a/scripts/rest-clients/resources-sample.xml +++ b/scripts/rest-clients/resources-sample.xml @@ -1,5 +1,10 @@ + + @@ -17,16 +22,62 @@ criterion-type-name="WORK_RELATIONSHIP" criterion-name="hiredResourceWorkingRelationship" start-date="2009-01-01" - finish-date=""/> + end-date=""/> - + + calendar-name="TestCalendar"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -37,12 +88,12 @@ criterion-type-name="WORK_RELATIONSHIP" criterion-name="hiredResourceWorkingRelationship" start-date="2009-01-01" - finish-date=""/> + end-date=""/> + end-date="2009-12-25"/>
@@ -55,7 +106,7 @@ + end-date="2009-12-25"/> @@ -66,7 +117,7 @@ criterion-type-name="WORK_RELATIONSHIP_XXX" criterion-name="hiredResourceWorkingRelationship" start-date="2009-01-01" - finish-date=""/> + end-date=""/> @@ -77,7 +128,7 @@ criterion-type-name="WORK_RELATIONSHIP" criterion-name="hiredResourceWorkingRelationshipXXX" start-date="2009-01-01" - finish-date=""/> + end-date=""/> @@ -87,7 +138,7 @@ + end-date=""/> @@ -97,7 +148,7 @@ + end-date=""/> @@ -105,8 +156,34 @@ and nif. --> - + + calendar-name="TestCalendar" /> + + + + + + + + + + + +