/* * This file is part of LibrePlan * * Copyright (C) 2012 WirelessGalicia, 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.expensesheet.entities; import java.math.BigDecimal; import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet; import org.apache.commons.lang3.StringUtils; import javax.validation.constraints.AssertTrue; import javax.validation.constraints.Min; import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.Valid; 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.ValidationException; import org.libreplan.business.expensesheet.daos.IExpenseSheetDAO; import org.libreplan.business.resources.entities.Resource; /** * ExpenseSheet Entity. * * @author Susana Montes Pedreira <smontes@wirelessgalicia.com> */ public class ExpenseSheet extends IntegrationEntity implements IHumanIdentifiable { private LocalDate firstExpense; private LocalDate lastExpense; private BigDecimal total; private String description; private boolean personal = false; @Valid private SortedSet<ExpenseSheetLine> expenseSheetLines = new TreeSet<>(new ExpenseSheetLineComparator()); private Integer lastExpenseSheetLineSequenceCode = 0; /** * Constructor for Hibernate. Do not use! */ protected ExpenseSheet() { } private ExpenseSheet(LocalDate firstExpense, LocalDate lastExpense, BigDecimal total) { this.setFirstExpense(firstExpense); this.setLastExpense(lastExpense); this.setTotal(total); } public static ExpenseSheet create() { return create(new ExpenseSheet()); } public static ExpenseSheet create(LocalDate firstExpense, LocalDate lastExpense, BigDecimal total) { return create(new ExpenseSheet(firstExpense, lastExpense, total)); } @Override protected IExpenseSheetDAO getIntegrationEntityDAO() { return Registry.getExpenseSheetDAO(); } protected void setFirstExpense(LocalDate firstExpense) { this.firstExpense = firstExpense; } public LocalDate getFirstExpense() { return firstExpense; } protected void setLastExpense(LocalDate lastExpense) { this.lastExpense = lastExpense; } public LocalDate getLastExpense() { return lastExpense; } protected void setTotal(BigDecimal total) { this.total = total; } @Min(message = "total must be greater or equal than 0", value = 0) @NotNull(message = "total not specified") public BigDecimal getTotal() { return total; } @NotEmpty(message = "the expense sheet must have least a expense sheet line.") @NotNull(message = "the expense sheet must have least a expense sheet line.") public SortedSet<ExpenseSheetLine> getExpenseSheetLines() { return Collections.unmodifiableSortedSet(expenseSheetLines); } /** Field setter of entity, do not remove! */ public void setLastExpenseSheetLineSequenceCode(Integer lastExpenseSheetLineSequenceCode) { this.lastExpenseSheetLineSequenceCode = lastExpenseSheetLineSequenceCode; } @NotNull(message = "last expense sheet line sequence code not specified") public Integer getLastExpenseSheetLineSequenceCode() { return lastExpenseSheetLineSequenceCode; } public void setDescription(String description) { this.description = description; } public String getDescription() { return description; } @AssertTrue(message = "The expense sheet line codes must be unique.") public boolean isNonRepeatedExpenseSheetLinesCodesConstraint() { return getFirstRepeatedCode(this.expenseSheetLines) == null; } public void generateExpenseSheetLineCodes(int numberOfDigits) { for (ExpenseSheetLine line : this.getExpenseSheetLines()) { if ((line.getCode() == null) || (line.getCode().isEmpty()) || (!line.getCode().startsWith(this.getCode()))) { this.incrementLastExpenseSheetLineSequenceCode(); String lineCode = EntitySequence.formatValue(numberOfDigits, this.getLastExpenseSheetLineSequenceCode()); line.setCode(this.getCode() + EntitySequence.CODE_SEPARATOR_CHILDREN + lineCode); } } } public void incrementLastExpenseSheetLineSequenceCode() { if (lastExpenseSheetLineSequenceCode == null) { lastExpenseSheetLineSequenceCode = 0; } lastExpenseSheetLineSequenceCode++; } public void keepSortedExpenseSheetLines(ExpenseSheetLine expenseSheetLine, LocalDate newDate) { this.expenseSheetLines.remove(expenseSheetLine); expenseSheetLine.setDate(newDate); this.expenseSheetLines.add(expenseSheetLine); } public void updateCalculatedProperties() { updateFistAndLastExpenseDate(); updateTotal(); } public void updateFistAndLastExpenseDate() { if (this.getExpenseSheetLines() != null && !this.getExpenseSheetLines().isEmpty()) { ExpenseSheetLine firstLine = this.getExpenseSheetLines().first(); setLastExpense(firstLine.getDate()); ExpenseSheetLine lastLine = this.getExpenseSheetLines().last(); setFirstExpense(lastLine.getDate()); } else { setLastExpense(null); setFirstExpense(null); } } public void updateTotal() { this.setTotal(new BigDecimal(0)); for (ExpenseSheetLine line : this.expenseSheetLines) { if (line.getValue() != null) { this.setTotal(this.getTotal().add(line.getValue())); } } } public void add(ExpenseSheetLine line) { this.expenseSheetLines.add(line); this.updateCalculatedProperties(); } public void remove(ExpenseSheetLine line) { this.expenseSheetLines.remove(line); this.updateCalculatedProperties(); } @Override public String getHumanId() { return getCode() + (description != null ? description : ""); } public ExpenseSheetLine getExpenseSheetLineByCode(String code) throws ValidationException { if (StringUtils.isBlank(code)) { throw new ValidationException("missing the code with which find the expense sheet line"); } for (ExpenseSheetLine l : this.expenseSheetLines) { if (l.getCode().equalsIgnoreCase(StringUtils.trim(code))) { return l; } } return null; } public boolean isPersonal() { return personal; } public void setPersonal(boolean personal) { this.personal = personal; } public boolean isNotPersonal() { return !personal; } @AssertTrue(message = "a personal expense sheet must have the same resource in all the lines") public boolean isPersonalExpenseSheetMustHaveTheSameResourceInAllLinesConstraint() { if (!personal) { return true; } if (expenseSheetLines.isEmpty()) { return true; } Resource resource = expenseSheetLines.iterator().next().getResource(); for (ExpenseSheetLine line : expenseSheetLines) { Resource resourceLine = line.getResource(); if ((resourceLine == null) || (!resourceLine.getId().equals(resource.getId()))) { return false; } } return true; } }