/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.libreplan.business.costcategories.entities; import static org.libreplan.business.i18n.I18nHelper._; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.validation.Valid; import javax.validation.constraints.AssertFalse; import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotNull; import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.constraints.NotEmpty; import org.joda.time.LocalDate; import org.libreplan.business.common.IHumanIdentifiable; import org.libreplan.business.common.IntegrationEntity; import org.libreplan.business.common.Registry; import org.libreplan.business.common.entities.EntitySequence; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.business.costcategories.daos.ICostCategoryDAO; /** * @author Jacobo Aragunde Perez <jaragunde@igalia.com> * @author Diego Pino García <dpino@igalia.com> */ public class CostCategory extends IntegrationEntity implements IHumanIdentifiable { private String name; private boolean enabled = true; private Integer lastHourCostSequenceCode = 0; @Valid private Set<HourCost> hourCosts = new HashSet<HourCost>(); private ICostCategoryDAO costCategoryDAO = Registry.getCostCategoryDAO(); // Default constructor, needed by Hibernate protected CostCategory() { } 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()); } public static CostCategory create(String name) { return (CostCategory) create(new CostCategory(name)); } public static void validateHourCostsOverlap(Set<HourCost> hoursCosts) throws ValidationException { List<HourCost> listHourCosts = new ArrayList<HourCost>(hoursCosts); for (int i = 0; i < listHourCosts.size(); i++) { LocalDate initDate = listHourCosts.get(i).getInitDate(); LocalDate endDate = listHourCosts.get(i).getEndDate(); for (int j = i + 1; j < listHourCosts.size(); j++) { HourCost listElement = listHourCosts.get(j); if (listElement.getType() == null || listHourCosts.get(i).getType() == null) { // this is not exactly an overlapping but a // problem with missing compulsory fields throw ValidationException.invalidValueException( "Hours cost type cannot be empty", listElement); } if (listElement.getType().getId() .equals(listHourCosts.get(i).getType().getId())) { if (initDate == null || listElement.getInitDate() == null) { // this is not exactly an overlapping but a // problem with missing compulsory fields throw ValidationException.invalidValueException( "Init date cannot be empty", listElement); } if (endDate == null && listElement.getEndDate() == null) { throw ValidationException.invalidValueException( "End date cannot be empty", listElement); } else if ((endDate == null && listElement.getEndDate() .compareTo(initDate) >= 0) || (listElement.getEndDate() == null && listElement .getInitDate().compareTo(endDate) <= 0)) { throw ValidationException .invalidValueException( _("Two Hour Cost of the same type overlap in time"), listElement); } else if ((endDate != null && listElement.getEndDate() != null) && ((listElement.getEndDate().compareTo(initDate) >= 0 && listElement .getEndDate().compareTo(endDate) <= 0) || (listElement .getInitDate().compareTo(initDate) >= 0 && listElement .getInitDate().compareTo(endDate) <= 0))) { throw ValidationException .invalidValueException( _("Two Hour Cost of the same type overlap in time"), listElement); } } } } } public static void validateCostCategoryOverlapping( List<ResourcesCostCategoryAssignment> costCategoryAssignments) { for (int i = 0; i < costCategoryAssignments.size(); i++) { LocalDate initDate = costCategoryAssignments.get(i).getInitDate(); LocalDate endDate = costCategoryAssignments.get(i).getEndDate(); for (int j = i + 1; j < costCategoryAssignments.size(); j++) { ResourcesCostCategoryAssignment costCategory = costCategoryAssignments .get(j); if (endDate == null && costCategory.getEndDate() == null) { throw ValidationException .invalidValueException( _("Some cost category assignments overlap in time"), costCategory); } else if ((endDate == null && costCategory.getEndDate() .compareTo(initDate) >= 0) || (costCategory.getEndDate() == null && costCategory .getInitDate().compareTo(endDate) <= 0)) { throw ValidationException .invalidValueException( _("Some cost category assignments overlap in time"), costCategory); } else if ((endDate != null && costCategory.getEndDate() != null) && ((costCategory.getEndDate().compareTo(initDate) >= 0 && // (1) // listElement.getEndDate() // inside // [initDate, // endDate] costCategory.getEndDate().compareTo(endDate) <= 0) || (costCategory.getInitDate().compareTo( initDate) >= 0 && // (2) // listElement.getInitDate() // inside [initDate, // endDate] costCategory.getInitDate().compareTo(endDate) <= 0) || (costCategory .getInitDate().compareTo(initDate) <= 0 && // (3) // [listElement.getInitDate(), // listElement.getEndDate()] costCategory.getEndDate().compareTo(endDate) >= 0))) { // contains // [initDate, // endDate] throw ValidationException .invalidValueException( _("Some cost category assignments overlap in time"), costCategory); } } } } protected CostCategory(String name) { this.name = name; } @NotEmpty(message = "name not specified") public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean getEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } @Valid public Set<HourCost> getHourCosts() { return hourCosts; } public void addHourCost(HourCost hourCost) { hourCosts.add(hourCost); if (hourCost.getCategory() != this) { hourCost.setCategory(this); } } public void removeHourCost(HourCost hourCost) { hourCosts.remove(hourCost); if (hourCost.getCategory() == this) { hourCost.setCategory(null); } } public boolean canAddHourCost(HourCost hourCost) { boolean overlap = false; LocalDate initDate = hourCost.getInitDate(); LocalDate endDate = hourCost.getEndDate(); for(HourCost listElement:hourCosts) { if(listElement.getType().getId().equals(hourCost.getType().getId())) { if (endDate == null && listElement.getEndDate() == null) { overlap = true; } else if((endDate == null && listElement.getEndDate().compareTo(initDate)>=0) || (listElement.getEndDate() == null && listElement.getInitDate().compareTo(endDate)<=0)) { overlap = true; } else if((endDate != null && listElement.getEndDate() != null) && ((listElement.getEndDate().compareTo(initDate)>=0 && listElement.getEndDate().compareTo(endDate)<=0) || (listElement.getInitDate().compareTo(initDate)>=0 && listElement.getInitDate().compareTo(endDate)<=0))) { overlap = true; } } } 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 Cost of the same type overlap in time") public boolean isHourCostsOverlapConstraint() { try { validateHourCostsOverlap(getHourCosts()); return false; } catch (ValidationException e) { return true; } } @Override protected ICostCategoryDAO getIntegrationEntityDAO() { return Registry.getCostCategoryDAO(); } @AssertTrue(message = "The hour cost codes must be unique.") public boolean isNonRepeatedHourCostCodesConstraint() { return getFirstRepeatedCode(this.hourCosts) == null; } public void generateHourCostCodes(int numberOfDigits) { for (HourCost hourCost : this.hourCosts) { if ((hourCost.getCode() == null) || (hourCost.getCode().isEmpty()) || (!hourCost.getCode().startsWith(this.getCode()))) { this.incrementLastHourCostSequenceCode(); String hourCostCode = EntitySequence.formatValue( numberOfDigits, this.getLastHourCostSequenceCode()); hourCost .setCode(this.getCode() + EntitySequence.CODE_SEPARATOR_CHILDREN + hourCostCode); } } } public BigDecimal getPriceCostByTypeOfWorkHour(String code) throws InstanceNotFoundException { if (StringUtils.isBlank(code)) { throw new InstanceNotFoundException(code, HourCost.class.getName()); } for (HourCost c : this.hourCosts) { if (c.getType().getCode().equalsIgnoreCase(StringUtils.trim(code))) { return c.getPriceCost(); } } throw new InstanceNotFoundException(code, HourCost.class.getName()); } public void incrementLastHourCostSequenceCode() { if (lastHourCostSequenceCode == null) { lastHourCostSequenceCode = 0; } lastHourCostSequenceCode++; } @NotNull(message = "last hours cost sequence code not specified") public Integer getLastHourCostSequenceCode() { return lastHourCostSequenceCode; } @Override public String getHumanId() { return name; } @AssertTrue(message = "the cost category name has to be unique and it is already in use") public boolean isUniqueNameConstraint() { if (StringUtils.isBlank(name)) { return true; } if (isNewObject()) { return !costCategoryDAO.existsByNameInAnotherTransaction(name); } else { return checkNotExistsOrIsTheSame(); } } private boolean checkNotExistsOrIsTheSame() { try { CostCategory costCategory = costCategoryDAO .findUniqueByNameInAnotherTransaction(name); return costCategory.getId().equals(getId()); } catch (InstanceNotFoundException e) { return true; } } }