/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* 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.kuali.kfs.module.tem.document.validation.impl;
import java.math.BigDecimal;
import org.kuali.kfs.module.tem.TemConstants.PerDiemType;
import org.kuali.kfs.module.tem.TemKeyConstants;
import org.kuali.kfs.module.tem.TemPropertyConstants;
import org.kuali.kfs.module.tem.businessobject.ActualExpense;
import org.kuali.kfs.module.tem.document.TravelDocument;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
import org.kuali.rice.krad.service.DataDictionaryService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
public class TravelDocumentActualExpenseDetailLineValidation extends TemDocumentExpenseLineValidation implements ActualExpenseDetailValidation {
protected ActualExpense actualExpenseForValidation;
protected ActualExpense actualExpenseDetailForValidation;
protected DataDictionaryService dataDictionaryService;
/**
* @see org.kuali.kfs.sys.document.validation.Validation#validate(org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent)
*/
@Override
public boolean validate(AttributedDocumentEvent event) {
TravelDocument travelDocument = (TravelDocument) event.getDocument();
boolean success = validateDetail(travelDocument);
return success;
}
/**
* Validate expense detail rules
*
* 1. expense detail mileage rule
* 2. expense detail amount is non-zero
* 3. expense detail does not exceed parent (threshold) for non-mileage expense
*
* @param actualExpense
* @param actualExpenseDetail
* @param travelDocument
* @return
*/
public boolean validateDetail(TravelDocument travelDocument){
boolean success = getDictionaryValidationService().isBusinessObjectValid(getActualExpenseDetailForValidation(), "");
if (success) {
//validate mileage
success &= validateMileageRules(travelDocument);
//validate lodging
success &= validateLodgingRules(travelDocument);
//validate expense detail amount is negative
if (getActualExpenseDetailForValidation().getExpenseAmount().isNegative()) {
GlobalVariables.getMessageMap().putError(TemPropertyConstants.EXPENSE_AMOUNT, TemKeyConstants.ERROR_TEM_DETAIL_LESS_THAN_ZERO);
success = false;
}
//for non-mileage expense detail
if (!getActualExpenseDetailForValidation().isMileage()){
// Determine if the detail is an amount that doesn't go over the threshold
if (getActualExpenseForValidation().getExpenseAmount().isLessThan(getActualExpenseForValidation().getTotalDetailExpenseAmount())) {
GlobalVariables.getMessageMap().putError(TemPropertyConstants.EXPENSE_AMOUNT, TemKeyConstants.ERROR_TEM_DETAIL_GREATER_THAN_EXPENSE);
success = false;
}
}
}
//info for non-one currency
if (success && !getActualExpenseDetailForValidation().getCurrencyRate().equals(BigDecimal.ONE)) {
GlobalVariables.getMessageMap().putInfo(TemPropertyConstants.EXPENSE_AMOUNT, TemKeyConstants.INFO_TEM_IMPORT_CURRENCY_CONVERSION);
}
return success;
}
/**
* This method validates following rules
*
* 1.Validates whether miles & mileage rate / other mileage rate is entered
* 2.Validates other mileage rate with the max rate configured in mileage table, if other mileage rate is specified
*
* @param actualExpense
* @param document
* @return boolean
*/
protected boolean validateMileageRules(TravelDocument document) {
boolean valid = true;
if (getActualExpenseDetailForValidation().isMileage()) {
// Check to see if miles & mileage rate/other mileage rate is entered
final java.sql.Date effectiveDate = document.getEffectiveDateForMileageRate(getActualExpenseDetailForValidation());
valid = (ObjectUtils.isNotNull(getActualExpenseDetailForValidation().getMiles()) && getActualExpenseDetailForValidation().getMiles() > 0 && (ObjectUtils.isNotNull(getActualExpenseDetailForValidation().getMileageRate(effectiveDate)) || (ObjectUtils.isNotNull(getActualExpenseDetailForValidation().getMileageOtherRate()) && getActualExpenseDetailForValidation().getMileageOtherRate().compareTo(BigDecimal.ZERO) > 0)));
if (valid) {
if (ObjectUtils.isNotNull(getActualExpenseDetailForValidation().getMileageOtherRate())) {
BigDecimal maxMileageRate = getMaxMileageRate();
if (getActualExpenseDetailForValidation().getMileageOtherRate().compareTo(maxMileageRate) > 0) {
GlobalVariables.getMessageMap().putError(TemPropertyConstants.TEM_ACTUAL_EXPENSE_MILES, TemKeyConstants.ERROR_ACTUAL_EXPENSE_OTHER_MILEAGE_RATE_EXCEED, getActualExpenseDetailForValidation().getMileageOtherRate().toString(), maxMileageRate.toString());
valid = false;
}
}
}
else {
String label = getDataDictionaryService().getAttributeLabel(ActualExpense.class, TemPropertyConstants.TEM_ACTUAL_EXPENSE_MILES) + ", " + getDataDictionaryService().getAttributeLabel(ActualExpense.class, TemPropertyConstants.TEM_ACTUAL_EXPENSE_MILE_RATE) + " or " + getDataDictionaryService().getAttributeLabel(ActualExpense.class, TemPropertyConstants.TEM_ACTUAL_EXPENSE_MILE_OTHER_RATE);
GlobalVariables.getMessageMap().putError(TemPropertyConstants.TEM_ACTUAL_EXPENSE_MILES, KFSKeyConstants.ERROR_REQUIRED, label);
}
// check that there's no per diem for the same day
if (isPerDiemMileageEntered(getActualExpenseDetailForValidation().getExpenseDate(), document.getPerDiemExpenses())) {
valid &= addPerDiemError(PerDiemType.mileage, false);
}
}
return valid;
}
/**
* If the actual expense detail line being validated is lodging, checks rules upon that
* @param document the travel document that the actual expense detail is being added to
* @return true if validation succeeded, false otherwise
*/
protected boolean validateLodgingRules(TravelDocument document) {
boolean valid = true;
if (getActualExpenseDetailForValidation().isLodging()) {
// check if there's a per diem the next day
if (isPerDiemLodgingEntered(getActualExpenseDetailForValidation().getExpenseDate(), document.getPerDiemExpenses())) {
valid &= addPerDiemError(PerDiemType.lodging, false);
}
}
return valid;
}
public ActualExpense getActualExpenseForValidation() {
return actualExpenseForValidation;
}
@Override
public void setActualExpenseForValidation(ActualExpense actualExpenseForValidation) {
this.actualExpenseForValidation = actualExpenseForValidation;
}
public ActualExpense getActualExpenseDetailForValidation() {
return actualExpenseDetailForValidation;
}
@Override
public void setActualExpenseDetailForValidation(ActualExpense actualExpenseDetailForValidation) {
this.actualExpenseDetailForValidation = actualExpenseDetailForValidation;
}
public DataDictionaryService getDataDictionaryService() {
return dataDictionaryService;
}
public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
this.dataDictionaryService = dataDictionaryService;
}
}