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 56de62831..46a6cb2ca 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 @@ -24,6 +24,7 @@ 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.IHourCostDAO; import org.navalplanner.business.costcategories.daos.IResourcesCostCategoryAssignmentDAO; import org.navalplanner.business.costcategories.daos.ITypeOfWorkHoursDAO; import org.navalplanner.business.externalcompanies.daos.IExternalCompanyDAO; @@ -149,6 +150,9 @@ public class Registry { @Autowired private IOrderElementTemplateDAO orderElementTemplateDAO; + @Autowired + private IHourCostDAO hourCostDAO; + private Registry() { } @@ -265,4 +269,8 @@ public class Registry { public static IOrderElementTemplateDAO getOrderElementTemplateDAO() { return getInstance().orderElementTemplateDAO; } + + public static IHourCostDAO getHourCostDAO() { + return getInstance().hourCostDAO; + } } 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 a2dd04acf..295d1e394 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 @@ -27,7 +27,7 @@ import java.util.List; 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.daos.IntegrationEntityDAO; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.costcategories.entities.CostCategory; import org.navalplanner.business.costcategories.entities.HourCost; @@ -44,7 +44,7 @@ import org.springframework.transaction.annotation.Transactional; */ @Repository @Scope(BeanDefinition.SCOPE_SINGLETON) -public class CostCategoryDAO extends GenericDAOHibernate +public class CostCategoryDAO extends IntegrationEntityDAO implements ICostCategoryDAO { @Override @@ -73,6 +73,27 @@ public class CostCategoryDAO extends GenericDAOHibernate } } + @Override + public CostCategory findUniqueByCode(String code) + throws InstanceNotFoundException { + if (code == null) { + throw new InstanceNotFoundException(code, CostCategory.class + .getName()); + } + + Criteria c = getSession().createCriteria(CostCategory.class).add( + Restrictions.eq("code", code).ignoreCase()); + CostCategory costCategory = (CostCategory) c.uniqueResult(); + + if (costCategory == null) { + throw new InstanceNotFoundException(code, 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/HourCostDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/HourCostDAO.java index 7c0465f99..b297aad8c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/HourCostDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/HourCostDAO.java @@ -20,7 +20,7 @@ package org.navalplanner.business.costcategories.daos; -import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.common.daos.IntegrationEntityDAO; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.costcategories.entities.HourCost; import org.springframework.beans.factory.config.BeanDefinition; @@ -32,7 +32,7 @@ import org.springframework.stereotype.Repository; */ @Repository @Scope(BeanDefinition.SCOPE_SINGLETON) -public class HourCostDAO extends GenericDAOHibernate implements +public class HourCostDAO extends IntegrationEntityDAO implements IHourCostDAO { @Override 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 28daa5c25..0c5704e08 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 @@ -22,7 +22,7 @@ package org.navalplanner.business.costcategories.daos; import java.util.List; -import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.common.daos.IIntegrationEntityDAO; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.costcategories.entities.CostCategory; @@ -30,10 +30,11 @@ import org.navalplanner.business.costcategories.entities.CostCategory; * @author Jacobo Aragunde Perez * @author Fernando Bellas Permuy */ -public interface ICostCategoryDAO extends IGenericDAO { +public interface ICostCategoryDAO extends IIntegrationEntityDAO { List findActive(); CostCategory findUniqueByName(String name) throws InstanceNotFoundException; + CostCategory findUniqueByCode(String code) throws InstanceNotFoundException; } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/IHourCostDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/IHourCostDAO.java index d7b5622d5..2500f94b1 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/IHourCostDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/daos/IHourCostDAO.java @@ -20,15 +20,14 @@ package org.navalplanner.business.costcategories.daos; -import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.common.daos.IIntegrationEntityDAO; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.costcategories.entities.HourCost; /** * @author Jacobo Aragunde Perez */ -public interface IHourCostDAO extends IGenericDAO { - +public interface IHourCostDAO extends IIntegrationEntityDAO { @Override public void remove(Long id) throws InstanceNotFoundException; } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/CostCategory.java b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/CostCategory.java index 82d51a306..8189a40d5 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/CostCategory.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/CostCategory.java @@ -25,16 +25,20 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.apache.commons.lang.StringUtils; import org.hibernate.validator.AssertFalse; import org.hibernate.validator.NotEmpty; import org.hibernate.validator.Valid; import org.joda.time.LocalDate; -import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.common.IntegrationEntity; +import org.navalplanner.business.common.Registry; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.costcategories.daos.ICostCategoryDAO; /** * @author Jacobo Aragunde Perez */ -public class CostCategory extends BaseEntity { +public class CostCategory extends IntegrationEntity { @NotEmpty private String name; @@ -48,6 +52,26 @@ public class CostCategory extends BaseEntity { } + public static CostCategory createUnvalidated(String code, String name, + Boolean enabled) { + CostCategory costCategory = create(new CostCategory(), code); + costCategory.name = name; + if (enabled != null) { + costCategory.enabled = enabled; + } + return costCategory; + + } + + public void updateUnvalidated(String name, Boolean enabled) { + if (!StringUtils.isBlank(name)) { + this.name = name; + } + if (enabled != null) { + this.enabled = enabled; + } + } + public static CostCategory create() { return (CostCategory) create(new CostCategory()); } @@ -118,6 +142,22 @@ public class CostCategory extends BaseEntity { return !overlap; } + public HourCost getHourCostByCode(String code) + throws InstanceNotFoundException { + + if (StringUtils.isBlank(code)) { + throw new InstanceNotFoundException(code, HourCost.class.getName()); + } + + for (HourCost c : this.hourCosts) { + if (c.getCode().equalsIgnoreCase(StringUtils.trim(code))) { + return c; + } + } + + throw new InstanceNotFoundException(code, HourCost.class.getName()); + } + @AssertFalse(message="Two hour costs with the same type overlap in time") public boolean checkHourCostsOverlap() { List listHourCosts = new ArrayList(); @@ -157,4 +197,10 @@ public class CostCategory extends BaseEntity { } return false; } + + @Override + protected ICostCategoryDAO getIntegrationEntityDAO() { + return Registry.getCostCategoryDAO(); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/HourCost.java b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/HourCost.java index add837c3c..9ae0feb06 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/HourCost.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/costcategories/entities/HourCost.java @@ -25,25 +25,23 @@ import java.math.BigDecimal; 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.IntegrationEntity; +import org.navalplanner.business.common.Registry; +import org.navalplanner.business.costcategories.daos.IHourCostDAO; /** * @author Jacobo Aragunde Perez */ -public class HourCost extends BaseEntity { +public class HourCost extends IntegrationEntity { - @NotNull private BigDecimal priceCost; - @NotNull private LocalDate initDate; private LocalDate endDate; - @NotNull private TypeOfWorkHours type; - @NotNull private CostCategory category; // Default constructor, needed by Hibernate @@ -51,6 +49,27 @@ public class HourCost extends BaseEntity { } + public static HourCost createUnvalidated(String code, BigDecimal priceCost, + LocalDate initDate) { + HourCost hourCost = create(new HourCost(), code); + if (priceCost != null) { + hourCost.setPriceCost(priceCost); + } + if (initDate != null) { + hourCost.setInitDate(initDate); + } + return hourCost; + } + + public void updateUnvalidated(BigDecimal priceCost, LocalDate initDate) { + if (priceCost != null) { + this.priceCost = priceCost; + } + if (initDate != null) { + this.initDate = initDate; + } + } + public static HourCost create(BigDecimal priceCost, LocalDate initDate) { return (HourCost) create(new HourCost(priceCost, initDate)); } @@ -64,6 +83,7 @@ public class HourCost extends BaseEntity { this.initDate = initDate; } + @NotNull(message = "price cost not specified") public BigDecimal getPriceCost() { return priceCost; } @@ -72,6 +92,7 @@ public class HourCost extends BaseEntity { this.priceCost = priceCost; } + @NotNull(message = "init date not specified") public LocalDate getInitDate() { return initDate; } @@ -88,6 +109,7 @@ public class HourCost extends BaseEntity { this.endDate = endDate; } + @NotNull(message = "type of work hours not specified") public TypeOfWorkHours getType() { return type; } @@ -96,6 +118,7 @@ public class HourCost extends BaseEntity { this.type = type; } + @NotNull(message = "cost category not specified") public CostCategory getCategory() { return category; } @@ -124,4 +147,9 @@ public class HourCost extends BaseEntity { } return (endDate.isAfter(initDate) || initDate.equals(endDate)); } + + @Override + protected IHourCostDAO getIntegrationEntityDAO() { + return Registry.getHourCostDAO(); + } } diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/costcategories/entities/CostCategories.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/costcategories/entities/CostCategories.hbm.xml index abde01e03..7938650a4 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/costcategories/entities/CostCategories.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/costcategories/entities/CostCategories.hbm.xml @@ -11,6 +11,7 @@ + @@ -30,6 +31,7 @@ + diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/CostCategoryDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/CostCategoryDTO.java new file mode 100644 index 000000000..95cb443b9 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/CostCategoryDTO.java @@ -0,0 +1,60 @@ +package org.navalplanner.ws.costcategories.api; + +import java.util.HashSet; +import java.util.Set; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; + +import org.navalplanner.business.costcategories.entities.CostCategory; +import org.navalplanner.ws.common.api.IntegrationEntityDTO; + +/** + * DTO for {@link CostCategory} entity. + * @author Susana Montes Pedreira + */ +public class CostCategoryDTO extends IntegrationEntityDTO { + + public final static String ENTITY_TYPE = "cost-category"; + + @XmlAttribute + public String name; + + @XmlAttribute + public Boolean enabled; + + @XmlElementWrapper(name = "hour-cost-list") + @XmlElement(name = "hour-cost") + public Set hourCostDTOs = new HashSet(); + + public CostCategoryDTO() { + } + + public CostCategoryDTO(String code, String name, Boolean enabled, + Set hourCostDTOs) { + + super(code); + this.name = name; + this.enabled = enabled; + this.hourCostDTOs = hourCostDTOs; + } + + /** + * This constructor automatically generates a unique code. It is intended to + * facilitate the implementation of test cases that add new instances (such + * instances will have a unique code). + */ + public CostCategoryDTO(String name, Boolean enabled, + Set hourCostDTOs) { + + this(generateCode(), name, enabled, hourCostDTOs); + + } + + @Override + public String getEntityType() { + return ENTITY_TYPE; + } + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/CostCategoryListDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/CostCategoryListDTO.java new file mode 100644 index 000000000..c0f1bd1d6 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/CostCategoryListDTO.java @@ -0,0 +1,46 @@ +/* + * This file is part of NavalPlan + * + * 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.costcategories.api; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * DTO for a list of CostCategory entities. + * @author Susana Montes Pedreira + */ +@XmlRootElement(name = "cost-category-list") +public class CostCategoryListDTO { + + @XmlElement(name = "cost-category") + public List costCategories = new ArrayList(); + + public CostCategoryListDTO() { + } + + public CostCategoryListDTO(List costCategories) { + this.costCategories = costCategories; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/HourCostDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/HourCostDTO.java new file mode 100644 index 000000000..b1f9ad32a --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/HourCostDTO.java @@ -0,0 +1,61 @@ +package org.navalplanner.ws.costcategories.api; + +import java.math.BigDecimal; +import java.util.Date; + +import javax.xml.bind.annotation.XmlAttribute; + +import org.navalplanner.business.costcategories.entities.HourCost; +import org.navalplanner.ws.common.api.IntegrationEntityDTO; + +/** + * DTO for {@link HourCost} entity. + * @author Susana Montes Pedreira + */ +public class HourCostDTO extends IntegrationEntityDTO { + + public final static String ENTITY_TYPE = "hour-cost"; + + @XmlAttribute + public BigDecimal priceCost; + + @XmlAttribute + public Date initDate; + + @XmlAttribute + public Date endDate; + + @XmlAttribute(name = "work-hours-type") + public String type; + + public HourCostDTO() { + } + + public HourCostDTO(String code, BigDecimal priceCost, Date initDate, + Date endDate, String type) { + + super(code); + this.initDate = initDate; + this.endDate = endDate; + this.priceCost = priceCost; + this.type = type; + } + + /** + * This constructor automatically generates a unique code. It is intended to + * facilitate the implementation of test cases that add new instances (such + * instances will have a unique code). + */ + public HourCostDTO(BigDecimal priceCost, Date initDate, Date endDate, + String type) { + + this(generateCode(), priceCost, initDate, endDate, type); + + } + + @Override + public String getEntityType() { + return ENTITY_TYPE; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/ICostCategoryService.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/ICostCategoryService.java new file mode 100644 index 000000000..8a5144d99 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/ICostCategoryService.java @@ -0,0 +1,35 @@ +/* + * This file is part of NavalPlan + * + * 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.costcategories.api; + +import org.navalplanner.ws.common.api.InstanceConstraintViolationsListDTO; + +/** + * Service for managing cost categories. + * @author Susana Montes Pedreira + */ +public interface ICostCategoryService { + + InstanceConstraintViolationsListDTO addCostCategories( + CostCategoryListDTO costCategoryListDTO); + + public CostCategoryListDTO getCostCotegories(); +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/package-info.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/package-info.java new file mode 100644 index 000000000..1f4f59c73 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/api/package-info.java @@ -0,0 +1,27 @@ +/* + * This file is part of NavalPlan + * + * 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 . + */ + +/** + * Specification of namespace for REST-based services. + * @author Susana Montes Pedreira + */ +@javax.xml.bind.annotation.XmlSchema(elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED, namespace = WSCommonGlobalNames.REST_NAMESPACE) +package org.navalplanner.ws.costcategories.api; +import org.navalplanner.ws.common.api.WSCommonGlobalNames; diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/impl/CostCategoryConverter.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/impl/CostCategoryConverter.java new file mode 100644 index 000000000..1323b527f --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/impl/CostCategoryConverter.java @@ -0,0 +1,194 @@ +/* + * This file is part of NavalPlan + * + * 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.costcategories.impl; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.navalplanner.business.common.Registry; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.costcategories.entities.CostCategory; +import org.navalplanner.business.costcategories.entities.HourCost; +import org.navalplanner.business.costcategories.entities.TypeOfWorkHours; +import org.navalplanner.ws.costcategories.api.CostCategoryDTO; +import org.navalplanner.ws.costcategories.api.HourCostDTO; + +/** + * Converter from/to cost-category-related entities to/from DTOs. + * @author Susana Montes Pedreira + */ +public class CostCategoryConverter { + + private CostCategoryConverter() { + } + + public final static CostCategoryDTO toDTO(CostCategory costCategory) { + + Set hourCostDTOs = new HashSet(); + + for (HourCost h : costCategory.getHourCosts()) { + hourCostDTOs.add(toDTO(h)); + } + + if (hourCostDTOs.isEmpty()) { + hourCostDTOs = null; + } + + return new CostCategoryDTO(costCategory.getCode(), costCategory + .getName(), costCategory.getEnabled(), hourCostDTOs); + + } + + public final static HourCostDTO toDTO(HourCost hourCost) { + + Date initDate = null; + if (hourCost.getInitDate() != null) { + initDate = hourCost.getInitDate().toDateTimeAtCurrentTime() + .toDate(); + } + + Date endDate = null; + if (hourCost.getEndDate() != null) { + endDate = hourCost.getEndDate().toDateTimeAtCurrentTime().toDate(); + } + + String type = null; + if (hourCost.getType() != null) { + type = hourCost.getType().getCode(); + } + + return new HourCostDTO(hourCost.getCode(), hourCost.getPriceCost(), + initDate, endDate, type); + + } + + public final static CostCategory toEntity(CostCategoryDTO costCategoryDTO) { + + CostCategory costCategory = CostCategory.createUnvalidated(StringUtils + .trim(costCategoryDTO.code), StringUtils + .trim(costCategoryDTO.name), costCategoryDTO.enabled); + + for (HourCostDTO hourCostDTO : costCategoryDTO.hourCostDTOs) { + HourCost hourCost = toEntity(hourCostDTO); + hourCost.setCategory(costCategory); + costCategory.addHourCost(hourCost); + } + + return costCategory; + + } + + private static HourCost toEntity(HourCostDTO hourCostDTO) + throws ValidationException { + + // Mandatory properties + LocalDate initDate = null; + if(hourCostDTO.initDate != null){ + initDate = LocalDate.fromDateFields(hourCostDTO.initDate); + } + + //Create new hour cost + HourCost hourCost = HourCost.createUnvalidated(hourCostDTO.code, hourCostDTO.priceCost, initDate); + + // optional properties + if (hourCostDTO.endDate != null) { + hourCost.setEndDate(LocalDate.fromDateFields(hourCostDTO.endDate)); + } + + if (hourCostDTO.type != null) { + try { + TypeOfWorkHours typeOfWorkHours = Registry + .getTypeOfWorkHoursDAO().findUniqueByCode( + hourCostDTO.type); + hourCost.setType(typeOfWorkHours); + } catch (InstanceNotFoundException e) { + throw new ValidationException( + _("There is no type of work hours with this code")); + } + } + return hourCost; + } + + public final static void updateCostCategory(CostCategory costCategory, + CostCategoryDTO costCategoryDTO) throws ValidationException { + /* + * 1: Update the existing work report line or add newwork report line. + */ + for (HourCostDTO hourCostDTO : costCategoryDTO.hourCostDTOs) { + + /* Step 1.1: requires each hour cost DTO to have a code. */ + if (StringUtils.isBlank(hourCostDTO.code)) { + throw new ValidationException(_("missing code in a hour cost")); + } + + try { + HourCost hourCost = costCategory + .getHourCostByCode(hourCostDTO.code); + updateHourCost(hourCost, hourCostDTO); + } catch (InstanceNotFoundException e) { + HourCost hourCost = toEntity(hourCostDTO); + hourCost.setCategory(costCategory); + costCategory.addHourCost(hourCost); + } + } + + /* 2: Update cost category basic properties. */ + costCategory.updateUnvalidated(StringUtils.trim(costCategoryDTO.name), + costCategoryDTO.enabled); + + } + + public final static void updateHourCost(HourCost hourCost, + HourCostDTO hourCostDTO) throws ValidationException { + + // Mandatory properties + LocalDate initDate = null; + if (hourCostDTO.initDate != null) { + initDate = LocalDate.fromDateFields(hourCostDTO.initDate); + } + + // Create new hour cost + hourCost.updateUnvalidated(hourCostDTO.priceCost, initDate); + + // optional properties + if (hourCostDTO.endDate != null) { + hourCost.setEndDate(LocalDate.fromDateFields(hourCostDTO.endDate)); + } + + if (hourCostDTO.type != null) { + try { + TypeOfWorkHours typeOfWorkHours = Registry + .getTypeOfWorkHoursDAO().findUniqueByCode( + hourCostDTO.type); + hourCost.setType(typeOfWorkHours); + } catch (InstanceNotFoundException e) { + throw new ValidationException( + _("There is no type of work hours with this code")); + } + } + } +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/impl/CostCategoryServiceREST.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/impl/CostCategoryServiceREST.java new file mode 100644 index 000000000..9ffa6cea1 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/costcategories/impl/CostCategoryServiceREST.java @@ -0,0 +1,96 @@ +/* + * This file is part of NavalPlan + * + * 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.costcategories.impl; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.navalplanner.business.common.daos.IIntegrationEntityDAO; +import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.costcategories.daos.ICostCategoryDAO; +import org.navalplanner.business.costcategories.entities.CostCategory; +import org.navalplanner.ws.common.api.InstanceConstraintViolationsListDTO; +import org.navalplanner.ws.common.impl.GenericRESTService; +import org.navalplanner.ws.costcategories.api.CostCategoryDTO; +import org.navalplanner.ws.costcategories.api.CostCategoryListDTO; +import org.navalplanner.ws.costcategories.api.ICostCategoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * REST-based implementation of ICostCategoryService. + * @author Susana Montes Pedreira + */ + +@Path("/costcategories/") +@Produces("application/xml") +@Service("costCategoryServiceREST") +public class CostCategoryServiceREST extends + GenericRESTService implements + ICostCategoryService { + + @Autowired + private ICostCategoryDAO costCategoryDAO; + + @Override + @GET + @Transactional(readOnly = true) + public CostCategoryListDTO getCostCotegories() { + return new CostCategoryListDTO(findAll()); + } + + @Override + @POST + @Consumes("application/xml") + public InstanceConstraintViolationsListDTO addCostCategories( + CostCategoryListDTO costCategoryListDTO) { + + return save(costCategoryListDTO.costCategories); + + } + + @Override + protected CostCategory toEntity(CostCategoryDTO entityDTO) { + return CostCategoryConverter.toEntity(entityDTO); + } + + @Override + protected CostCategoryDTO toDTO(CostCategory entity) { + return CostCategoryConverter.toDTO(entity); + } + + @Override + protected IIntegrationEntityDAO getIntegrationEntityDAO() { + return costCategoryDAO; + } + + @Override + protected void updateEntity(CostCategory entity, CostCategoryDTO entityDTO) + throws ValidationException { + + CostCategoryConverter.updateCostCategory(entity, entityDTO); + + } +} 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 1ef54149b..95217476f 100644 --- a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml +++ b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml @@ -65,6 +65,7 @@ + diff --git a/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/costcategories/CostCategoryServiceTest.java b/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/costcategories/CostCategoryServiceTest.java new file mode 100644 index 000000000..34eb07c98 --- /dev/null +++ b/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/costcategories/CostCategoryServiceTest.java @@ -0,0 +1,272 @@ +/* + * This file is part of NavalPlan + * + * 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.web.test.ws.costcategories; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; +import static org.navalplanner.web.WebappGlobalNames.WEBAPP_SPRING_CONFIG_FILE; +import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import org.hibernate.SessionFactory; +import org.joda.time.LocalDate; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.costcategories.daos.ICostCategoryDAO; +import org.navalplanner.business.costcategories.daos.IHourCostDAO; +import org.navalplanner.business.costcategories.daos.ITypeOfWorkHoursDAO; +import org.navalplanner.business.costcategories.entities.CostCategory; +import org.navalplanner.business.costcategories.entities.HourCost; +import org.navalplanner.business.costcategories.entities.TypeOfWorkHours; +import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO; +import org.navalplanner.ws.costcategories.api.CostCategoryDTO; +import org.navalplanner.ws.costcategories.api.CostCategoryListDTO; +import org.navalplanner.ws.costcategories.api.HourCostDTO; +import org.navalplanner.ws.costcategories.api.ICostCategoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * Tests for ICostCategoryService. + * @author Susana Montes Pedreira + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE, + WEBAPP_SPRING_CONFIG_FILE, WEBAPP_SPRING_CONFIG_TEST_FILE }) +@Transactional +public class CostCategoryServiceTest { + + @Autowired + private ICostCategoryService costCategoryService; + + @Autowired + private ICostCategoryDAO costCategoryDAO; + + @Autowired + private IHourCostDAO hourCostDAO; + + @Autowired + private ITypeOfWorkHoursDAO typeOfWorkHoursDAO; + + @Autowired + private SessionFactory sessionFactory; + + private final String typeOfWorkHoursCodeA = "code-A"; + + private final String typeOfWorkHoursCodeB = "code-B"; + + @Test + @Rollback(false) + public void createAPairTypeOfWorkHours() { + givenTypeOfWorkHours(typeOfWorkHoursCodeA); + givenTypeOfWorkHours(typeOfWorkHoursCodeB); + } + + private void givenTypeOfWorkHours(String code) { + + TypeOfWorkHours typeOfWorkHours = TypeOfWorkHours.create(); + + typeOfWorkHours.setCode(code); + typeOfWorkHours.setName("name" + UUID.randomUUID()); + + typeOfWorkHoursDAO.save(typeOfWorkHours); + typeOfWorkHoursDAO.flush(); + sessionFactory.getCurrentSession().evict(typeOfWorkHours); + typeOfWorkHours.dontPoseAsTransientObjectAnymore(); + } + + @Test + public void testAddAndGetCostCategories() { + + /* Build cost category (5 constraint violations). */ + + // Missing cost category name and enabled. + CostCategoryDTO cc1 = new CostCategoryDTO(null, true, + new HashSet()); + // Valid cost category DTO without hour cost + CostCategoryDTO cc2 = new CostCategoryDTO("cc2", + true, new HashSet()); + + // Valid cost category DTO with a hour cost + Set cc3_HourCostDTOs = new HashSet(); + HourCostDTO cc3_1_HourCostDTO = new HourCostDTO(new BigDecimal(3), + new Date(), new Date(), typeOfWorkHoursCodeA); + cc3_HourCostDTOs.add(cc3_1_HourCostDTO); + CostCategoryDTO cc3 = new CostCategoryDTO("cc3", true, cc3_HourCostDTOs); + + // Valid cost category DTO with a invalid hour cost + // missing pricecost, initDate and type + Set cc4_HourCostDTOs = new HashSet(); + HourCostDTO cc4_1_HourCostDTO = new HourCostDTO(null, null, null, null); + cc4_HourCostDTOs.add(cc4_1_HourCostDTO); + CostCategoryDTO cc4 = new CostCategoryDTO("cc4", true, cc4_HourCostDTOs); + + /* Criterion type list. */ + CostCategoryListDTO costCategoryListDTO = createCostCategoryListDTO( + cc1, cc2, cc3, cc4); + + List instanceConstraintViolationsList = costCategoryService + .addCostCategories(costCategoryListDTO).instanceConstraintViolationsList; + + assertTrue(instanceConstraintViolationsList.toString(), + instanceConstraintViolationsList.size() == 2); + assertTrue(instanceConstraintViolationsList.get(0).constraintViolations + .toString(), + instanceConstraintViolationsList.get(0).constraintViolations + .size() == 1); // cc1 constraint violations. + + assertTrue(instanceConstraintViolationsList.get(1).constraintViolations + .toString(), + instanceConstraintViolationsList.get(1).constraintViolations + .size() == 3); // cc1 constraint violations. + + /* Test. */ + assertFalse(costCategoryDAO.existsByCode(cc1.code)); + assertTrue(costCategoryDAO.existsByCode(cc2.code)); + assertTrue(costCategoryDAO.existsByCode(cc3.code)); + assertFalse(costCategoryDAO.existsByCode(cc4.code)); + + try { + CostCategory costCategory = costCategoryDAO.findByCode(cc3.code); + assertTrue(costCategory.getHourCosts().size() == 1); + } catch (InstanceNotFoundException e) { + assertTrue(false); + } + } + + @Test + public void testUpdateCostCategory() throws InstanceNotFoundException { + + //First one it creates Valid cost category DTO with a hour cost + final String costCategoryCode = "code-CC"; + final String hourCostCode = "code-HC"; + + Set cc1_HourCostDTOs = new HashSet(); + HourCostDTO cc1_1_HourCostDTO = new HourCostDTO(hourCostCode,new BigDecimal(3), + new Date(), new Date(), typeOfWorkHoursCodeA); + cc1_HourCostDTOs.add(cc1_1_HourCostDTO); + CostCategoryDTO cc1 = new CostCategoryDTO(costCategoryCode,"newCC1", true, cc1_HourCostDTOs); + + /* Criterion type list. */ + CostCategoryListDTO costCategoryListDTO = createCostCategoryListDTO(cc1); + + List instanceConstraintViolationsList = costCategoryService + .addCostCategories(costCategoryListDTO).instanceConstraintViolationsList; + costCategoryDAO.flush(); + hourCostDAO.flush(); + + /* Test. */ + assertTrue(instanceConstraintViolationsList.toString(), + instanceConstraintViolationsList.size() == 0); + assertTrue(costCategoryDAO.existsByCode(cc1.code)); + CostCategory costCategory = null; + try { + costCategory = costCategoryDAO.findByCode(cc1.code); + assertTrue(costCategory.getHourCosts().size() == 1); + } catch (InstanceNotFoundException e) { + assertTrue(false); + } + costCategoryDAO.flush(); + sessionFactory.getCurrentSession().evict(costCategory); + costCategory.dontPoseAsTransientObjectAnymore(); + + // Update the previous cost category + Set cc2_HourCostDTOs = new HashSet(); + HourCostDTO cc2_1_HourCostDTO = new HourCostDTO(hourCostCode, + new BigDecimal(100), getNextMonthDate(), getNextMonthDate(), + typeOfWorkHoursCodeB); + cc2_HourCostDTOs.add(cc2_1_HourCostDTO); + CostCategoryDTO cc2 = new CostCategoryDTO(costCategoryCode,"updateCC1", false, cc2_HourCostDTOs); + + /* Criterion type list. */ + costCategoryListDTO = createCostCategoryListDTO(cc2); + + instanceConstraintViolationsList = costCategoryService + .addCostCategories(costCategoryListDTO).instanceConstraintViolationsList; + + /* Test. */ + // the end date cannot be before the initDate + assertTrue(instanceConstraintViolationsList.toString(), + instanceConstraintViolationsList.size() == 0); + assertTrue(costCategoryDAO.existsByCode(cc1.code)); + assertTrue(hourCostDAO.existsByCode(cc1_1_HourCostDTO.code)); + + // Check if the changes was updated + try { + costCategory = costCategoryDAO + .findByCode(costCategoryCode); + assertTrue(costCategory.getHourCosts().size() == 1); + assertTrue(costCategory.getName().equalsIgnoreCase("updateCC1")); + assertFalse(costCategory.getEnabled()); + + HourCost hourCost = hourCostDAO.findByCode(cc1_1_HourCostDTO.code); + LocalDate nextMonthDate = LocalDate + .fromDateFields(getNextMonthDate()); + assertTrue(hourCost.getInitDate().compareTo(nextMonthDate) == 0); + assertFalse(hourCost.getEndDate() == null); + assertTrue(hourCost.getPriceCost().compareTo(new BigDecimal(100)) == 0); + assertTrue(hourCost.getType().getCode().equalsIgnoreCase( + typeOfWorkHoursCodeB)); + } catch (InstanceNotFoundException e) { + assertTrue(false); + } + } + + private Date getNextMonthDate() { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date()); + calendar.add(calendar.MONTH, 1); + + int date = calendar.get(Calendar.DAY_OF_MONTH); + int month = calendar.get(calendar.MONTH); + int year = calendar.get(Calendar.YEAR); + + calendar.set(year, month, date); + return calendar.getTime(); + } + + private CostCategoryListDTO createCostCategoryListDTO( + CostCategoryDTO... costCategories) { + + List costCategoryList = new ArrayList(); + + for (CostCategoryDTO c : costCategories) { + costCategoryList.add(c); + } + + return new CostCategoryListDTO(costCategoryList); + + } + +} diff --git a/scripts/rest-clients/cost-categories-sample.xml b/scripts/rest-clients/cost-categories-sample.xml new file mode 100644 index 000000000..56a026b76 --- /dev/null +++ b/scripts/rest-clients/cost-categories-sample.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/scripts/rest-clients/export-cost-categories.sh b/scripts/rest-clients/export-cost-categories.sh new file mode 100755 index 000000000..5d1b9b866 --- /dev/null +++ b/scripts/rest-clients/export-cost-categories.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +. ./rest-common-env.sh + +printf "Login name: " +read loginName +printf "Password: " +read password + +if [ "$1" = "--prod" ]; then + baseServiceURL=$PRODUCTION_BASE_SERVICE_URL + certificate=$PRODUCTION_CERTIFICATE +else + baseServiceURL=$DEVELOPMENT_BASE_SERVICE_URL + certificate=$DEVELOPMENT_CERTIFICATE +fi + +authorization=`./base64.sh $loginName:$password` + +curl -sv -X GET $certificate --header "Authorization: Basic $authorization" \ + $baseServiceURL/costcategories | tidy -xml -i -q -utf8 diff --git a/scripts/rest-clients/import-cost-categories.sh b/scripts/rest-clients/import-cost-categories.sh new file mode 100755 index 000000000..470f492b6 --- /dev/null +++ b/scripts/rest-clients/import-cost-categories.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +. ./rest-common-env.sh + +printf "Login name: " +read loginName +printf "Password: " +read password + +baseServiceURL=$DEVELOPMENT_BASE_SERVICE_URL +certificate=$DEVELOPMENT_CERTIFICATE + +for i in "$@" +do + if [ "$i" = "--prod" ]; then + baseServiceURL=$PRODUCTION_BASE_SERVICE_URL + certificate=$PRODUCTION_CERTIFICATE + else + file=$i + fi +done + +if [ "$file" = "" ]; then + printf "Missing file\n" 1>&2 + exit 1 +fi + +authorization=`./base64.sh $loginName:$password` + +curl -sv -X POST $certificate -d @$file \ + --header "Content-type: application/xml" \ + --header "Authorization: Basic $authorization" \ + $baseServiceURL/costcategories | tidy -xml -i -q -utf8