/*
* 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.ec.document.validation.impl;
import static org.kuali.kfs.sys.KFSConstants.CurrencyTypeAmounts.HUNDRED_DOLLAR_AMOUNT;
import static org.kuali.kfs.sys.businessobject.AccountingLineOverride.CODE.EXPIRED_ACCOUNT;
import static org.kuali.kfs.sys.businessobject.AccountingLineOverride.CODE.EXPIRED_ACCOUNT_AND_NON_FRINGE_ACCOUNT_USED;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.A21SubAccount;
import org.kuali.kfs.coa.businessobject.Account;
import org.kuali.kfs.module.ec.EffortConstants;
import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail;
import org.kuali.kfs.module.ec.document.EffortCertificationDocument;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.ObjectUtil;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kns.service.DictionaryValidationService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Provides a set of facilities to determine whether the given Effort Certification Documents or Effort Certification Detail meet
* the specified requirements.
*/
public class EffortCertificationDocumentRuleUtil {
private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EffortCertificationDocumentRuleUtil.class);
/**
* reset the attribute with the blank value to the default values
*
* @param detailLine the given detail line
*/
public static void applyDefaultValues(EffortCertificationDetail detailLine) {
if (StringUtils.isBlank(detailLine.getSubAccountNumber())) {
detailLine.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
}
if (StringUtils.isBlank(detailLine.getCostShareSourceSubAccountNumber())) {
detailLine.setCostShareSourceSubAccountNumber(KFSConstants.getDashSubAccountNumber());
}
if (StringUtils.isBlank(detailLine.getSourceChartOfAccountsCode())) {
detailLine.setSourceChartOfAccountsCode(EffortConstants.DASH_CHART_OF_ACCOUNTS_CODE);
}
if (StringUtils.isBlank(detailLine.getSourceAccountNumber())) {
detailLine.setSourceAccountNumber(EffortConstants.DASH_ACCOUNT_NUMBER);
}
if (ObjectUtils.isNull(detailLine.getEffortCertificationPayrollAmount())) {
detailLine.setEffortCertificationPayrollAmount(KualiDecimal.ZERO);
}
if (ObjectUtils.isNull(detailLine.getEffortCertificationOriginalPayrollAmount())) {
detailLine.setEffortCertificationOriginalPayrollAmount(KualiDecimal.ZERO);
}
if (ObjectUtils.isNull(detailLine.getEffortCertificationCalculatedOverallPercent())) {
detailLine.setEffortCertificationCalculatedOverallPercent(0);
}
if (ObjectUtils.isNull(detailLine.getEffortCertificationUpdatedOverallPercent())) {
detailLine.setEffortCertificationUpdatedOverallPercent(0);
}
if(ObjectUtils.isNull(detailLine.getUniversityFiscalYear())){
UniversityDateService universityDateService = SpringContext.getBean(UniversityDateService.class);
if(detailLine.getUniversityFiscalYear() == null) {
detailLine.setUniversityFiscalYear(universityDateService.getCurrentFiscalYear());
}
}
}
/**
* determine whether the expired account in the detail line can be used.
*
* @param detailLine the given detail line
* @return true if the expired account in the detail line can be used; otherwise, false
*/
public static boolean canExpiredAccountBeUsed(EffortCertificationDetail detailLine) {
Account account = detailLine.getAccount();
boolean canExpiredAccountUsed = true;
if (ObjectUtils.isNotNull(account) && account.isExpired()) {
String overrideCode = detailLine.getOverrideCode();
canExpiredAccountUsed = Arrays.asList(EXPIRED_ACCOUNT, EXPIRED_ACCOUNT_AND_NON_FRINGE_ACCOUNT_USED).contains(overrideCode);
}
return canExpiredAccountUsed;
}
/**
* determine if the sub account associated with the given detail line is a valid A21 sub account
*
* @param detailLine the given detail line
* @return true if the sub account associated with the given detail line is a valid A21 sub account; otherwise, false
*/
public static boolean hasA21SubAccount(EffortCertificationDetail detailLine) {
String subAccountNumber = detailLine.getSubAccountNumber();
if (KFSConstants.getDashSubAccountNumber().equals(subAccountNumber)) {
return false;
}
return ObjectUtils.isNotNull(detailLine.getSubAccount().getA21SubAccount());
}
/**
* determine if the given detail line is associated with a closed account
*
* @param detailLine the given detail line
* @return true if the given detail line is associated with a closed account; otherwise, false
*/
public static boolean hasClosedAccount(EffortCertificationDetail detailLine) {
return !detailLine.getAccount().isActive();
}
/**
* determine if the given detail line is associated with a contract grant account
*
* @param detailLine the given detail line
* @return true if the given detail line is associated with a contract grant account; otherwise, false
*/
public static boolean hasContractGrantAccount(EffortCertificationDetail detailLine) {
return detailLine.getAccount().isForContractsAndGrants();
}
/**
* determine if the given detail line is associated with a sub account whose type code is in the given list
*
* @param detailLine the given detail line
* @param designatedCostShareSubAccountTypeCode the designated cost share sub account type codes
* @return true if the given detail line is associated with a sub account whose type code is in the given list; otherwise, false
*/
public static boolean hasCostShareSubAccount(EffortCertificationDetail detailLine, List<String> designatedCostShareSubAccountTypeCodes) {
if (!hasA21SubAccount(detailLine)) {
return false;
}
String costShareSubAccountTypeCode = detailLine.getSubAccount().getA21SubAccount().getSubAccountTypeCode();
return designatedCostShareSubAccountTypeCodes.contains(costShareSubAccountTypeCode);
}
/**
* determine if the payroll amount of the given detail line is not negative
*
* @param detailLine the given detail line
* @return true if the payroll amount of the given detail line is not negative; otherwise, false
*/
public static boolean hasNonnegativePayrollAmount(EffortCertificationDetail detailLine) {
KualiDecimal payrollAmount = detailLine.getEffortCertificationPayrollAmount();
return ObjectUtils.isNotNull(payrollAmount) && isPayrollAmountNonnegative(payrollAmount);
}
/**
* determine if there is a line in the given document that has the same values for the comparable fields as the given detail
* line
*
* @param document the given effort certification document
* @param detailLine the given detail line
* @param comparableFields the comparable fields
* @return true if there is a line in the given document that has the same values for the comparable fields as the given detail
* line; otherwise, false
*/
public static boolean hasSameExistingLine(EffortCertificationDocument document, EffortCertificationDetail detailLine, List<String> comparableFields) {
List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines();
for (EffortCertificationDetail line : detailLines) {
if(detailLine != line && ObjectUtil.equals(line, detailLine, comparableFields)) {
return true;
}
}
return false;
}
/**
* determine if the given detail line has a valid effort percentage. The percentage should be between 0 and 100.
*
* @param detailLine the given detail line
* @return true if the given detail line has a valid effort percentage; otherwise, false
*/
public static boolean hasValidEffortPercent(EffortCertificationDetail detailLine) {
Integer effortPercent = detailLine.getEffortCertificationUpdatedOverallPercent();
return ObjectUtils.isNotNull(effortPercent) && isValidPercent(effortPercent);
}
/**
* determine if the fields in the detail line are in the correct formats defined in the data dictionary
*
* @param detailLine the given detail line
* @return true if the fields in the detail line are in the correct formats defined in the data dictionary; otherwise, false
*/
public static boolean hasValidFormat(EffortCertificationDetail detailLine) {
int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount();
SpringContext.getBean(DictionaryValidationService.class).validateBusinessObject(detailLine);
int currentErrorCount = GlobalVariables.getMessageMap().getErrorCount();
return currentErrorCount == originalErrorCount;
}
/**
* determine if there is a change on the payroll amount of the given detail line comparing to its original payroll amount
*
* @param detailLine the given effort certification detail line
* @return true if there is a change on the payroll amount of the given detail line comparing to its original payroll amount
*/
public static boolean isPayrollAmountChangedFromOriginal(EffortCertificationDetail detailLine) {
KualiDecimal payrollAmount = detailLine.getEffortCertificationPayrollAmount();
KualiDecimal originalPayrollAmount = detailLine.getEffortCertificationOriginalPayrollAmount();
KualiDecimal difference = originalPayrollAmount.subtract(payrollAmount);
return difference.isNonZero();
}
/**
* determine if there is a change on the payroll amount of the given document
*
* @param document the given effort certification document
* @return true if there is the change on the payroll amount of any detail line in the given document
*/
public static boolean isPayrollAmountChangedFromOriginal(EffortCertificationDocument document) {
List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines();
for (EffortCertificationDetail line : detailLines) {
if (isPayrollAmountChangedFromOriginal(line)) {
return true;
}
}
return false;
}
/**
* determine if there is a change on the payroll amount of the given detail line comparing to its persisted payroll amount
*
* @param detailLine the given effort certification detail line
* @return true if there is a change on the payroll amount of the given detail line comparing to its persisted payroll amount
*/
public static boolean isPayrollAmountChangedFromPersisted(EffortCertificationDetail detailLine) {
KualiDecimal persistedAmount = detailLine.getPersistedPayrollAmount();
KualiDecimal difference = KualiDecimal.ZERO;
if (ObjectUtils.isNotNull(persistedAmount)) {
KualiDecimal payrollAmount = detailLine.getEffortCertificationPayrollAmount();
difference = persistedAmount.subtract(payrollAmount);
}
return difference.isNonZero();
}
/**
* determine if there is a change on the payroll amount of the given document
*
* @param document the given effort certification document
* @return true if there is the change on the payroll amount of any detail line in the given document
*/
public static boolean isPayrollAmountChangedFromPersisted(EffortCertificationDocument document) {
List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines();
for (EffortCertificationDetail line : detailLines) {
if (isPayrollAmountChangedFromPersisted(line)) {
return true;
}
}
return false;
}
/**
* determine if there is a change on the payroll amount of the given detail line comparing to its persisted payroll amount
*
* @param detailLine the given effort certification detail line
* @return true if there is a change on the payroll amount of the given detail line comparing to its persisted payroll amount
*/
public static boolean isEffortPercentChangedFromPersisted(EffortCertificationDetail detailLine) {
Integer persistedAmount = detailLine.getPersistedEffortPercent();
Integer effortPercent = detailLine.getEffortCertificationUpdatedOverallPercent();
return !persistedAmount.equals(effortPercent);
}
/**
* determine if there is a change on the payroll amount of the given document
*
* @param document the given effort certification document
* @return true if there is the change on the payroll amount of any detail line in the given document
*/
public static boolean isEffortPercentChangedFromPersisted(EffortCertificationDocument document) {
List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines();
for (EffortCertificationDetail line : detailLines) {
if (isEffortPercentChangedFromPersisted(line)) {
return true;
}
}
return false;
}
/**
* determine if the given payroll amount is greater than and equal to 0
*
* @param payrollAmount the given payroll amount
* @return true if the given payroll amount is greater than and equal to 0; otherwise, false
*/
public static boolean isPayrollAmountNonnegative(KualiDecimal payrollAmount) {
return payrollAmount.isGreaterEqual(KualiDecimal.ZERO);
}
/**
* determine if original effort percent is same as the current effort percent for the given detail line
* @param detailLine the given effort certification detail line
* @return true if original effort percent same as current effort percent
*/
public static boolean isOriginalEffortPercentSameAsCurrentEffortPercent(Integer originalEffortPercent, Integer effortPercent) {
return originalEffortPercent.equals(effortPercent);
}
/**
* determine if the change on the payroll amount of the given detail line exceeds the specified limit
*
* @param detailLine the given effort certification detail line
* @param limitOfLinePayrollAmountChange the specified upper bound limit
* @return true if the change on the payroll amount of the given detail line exceeds the specified limit; otherwise, false
*/
public static boolean isPayrollAmountOverChanged(EffortCertificationDetail detailLine, KualiDecimal originalTotalAmount, double limitOfLinePayrollAmountChange) {
KualiDecimal payrollAmount = detailLine.getEffortCertificationPayrollAmount();
KualiDecimal originalPayrollAmount = detailLine.getEffortCertificationOriginalPayrollAmount();
KualiDecimal difference = KualiDecimal.ZERO;
Integer originalEffortPercent = detailLine.getEffortCertificationCalculatedOverallPercent();
Integer effortPercent = detailLine.getEffortCertificationUpdatedOverallPercent();
if (isOriginalEffortPercentSameAsCurrentEffortPercent(originalEffortPercent, effortPercent)) {
difference = originalPayrollAmount.subtract(payrollAmount).multiply(HUNDRED_DOLLAR_AMOUNT).abs();
return difference.divide(originalTotalAmount).doubleValue() > limitOfLinePayrollAmountChange * HUNDRED_DOLLAR_AMOUNT.intValue();
}
return false;
}
/**
* determine if there is a change on the payroll amount of a detail line that exceeds the specified limit
*
* @param document the given effort certification document
* @param limitOfLinePayrollAmountChange the specified upper bound limit
* @return true if the change on the payroll amount of any detail line exceeds the specified limit; otherwise, false
*/
public static boolean isPayrollAmountOverChanged(EffortCertificationDocument document, double limitOfLinePayrollAmountChange) {
List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines();
KualiDecimal originalTotalAmount = document.getTotalOriginalPayrollAmount();
for (EffortCertificationDetail line : detailLines) {
if (isPayrollAmountOverChanged(line, originalTotalAmount, limitOfLinePayrollAmountChange)) {
return true;
}
}
return false;
}
/**
* detrmine if the total effort percent of the given document is 100
*
* @param document the given effort certification document
* @return true if the total effort percent of the given document is 100
*/
public static boolean isTotalEffortPercentageAs100(EffortCertificationDocument document) {
return document.getTotalEffortPercent() == 100;
}
/**
* determine if the change on the total payroll amount exceeds the specified limit
*
* @param document the given effort certification document
* @param limitOfTotalPayrollAmountChange the specified upper bound limit
* @return true if the change on the total payroll amount exceeds the specified limit; otherwise, false
*/
public static boolean isTotalPayrollAmountOverChanged(EffortCertificationDocument document, double limitOfTotalPayrollAmountChange) {
KualiDecimal totalPayrollAmount = document.getTotalPayrollAmount();
KualiDecimal totalOriginalPayrollAmount = document.getTotalOriginalPayrollAmount();
KualiDecimal difference = totalOriginalPayrollAmount.subtract(totalPayrollAmount).abs();
return difference.doubleValue() > limitOfTotalPayrollAmountChange;
}
/**
* determine if the given percent is between 0 and 100.
*
* @param percent the given percent
* @return true if the given percent is between 0 and 100; otherwise, false
*/
public static boolean isValidPercent(Integer percent) {
return percent >= 0 && percent <= 100;
}
/**
* update the information of the source attributes for the given detail line
*
* @param detailLine the given detail line
*/
public static void updateSourceAccountInformation(EffortCertificationDetail detailLine) {
A21SubAccount a21SubAccount = detailLine.getSubAccount().getA21SubAccount();
if (ObjectUtils.isNotNull(a21SubAccount)) {
detailLine.setSourceChartOfAccountsCode(a21SubAccount.getCostShareChartOfAccountCode());
detailLine.setSourceAccountNumber(a21SubAccount.getCostShareSourceAccountNumber());
detailLine.setCostShareSourceSubAccountNumber(a21SubAccount.getCostShareSourceSubAccountNumber());
}
}
/**
* determine if there is a line associated with the given document
*
* @param document the given effort certification document
* @return true if there is a line associated with the given document; otherwise, false
*/
public static boolean hasDetailLine(EffortCertificationDocument document) {;
List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines();
return detailLines != null && !detailLines.isEmpty();
}
}