/*
* 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.cg.document.validation.impl;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.integration.ar.AccountsReceivableModuleBillingService;
import org.kuali.kfs.module.cg.businessobject.Agency;
import org.kuali.kfs.module.cg.businessobject.AwardFundManager;
import org.kuali.kfs.module.cg.businessobject.CGProjectDirector;
import org.kuali.kfs.module.cg.businessobject.Primaryable;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.kim.api.identity.CodedAttribute;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.kim.api.role.RoleService;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.bo.BusinessObject;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Rules for the Proposal/Award/Agency maintenance document.
*/
public class CGMaintenanceDocumentRuleBase extends MaintenanceDocumentRuleBase {
protected static final String PROJECT_DIRECTOR_DECEASED = "D";
protected static final String[] PROJECT_DIRECTOR_INVALID_STATUSES = { PROJECT_DIRECTOR_DECEASED };
protected static final String AGENCY_TYPE_CODE_FEDERAL = "F";
protected boolean contractsGrantsBillingEnhancementActive;
public CGMaintenanceDocumentRuleBase() {
super();
contractsGrantsBillingEnhancementActive = SpringContext.getBean(AccountsReceivableModuleBillingService.class).isContractsGrantsBillingEnhancementActive();
}
/**
* Checks to see if the end date is after the begin date
*
* @param begin
* @param end
* @param propertyName
* @return true if end is after begin, false otherwise
*/
protected boolean checkEndAfterBegin(Date begin, Date end, String propertyName) {
boolean success = true;
if (ObjectUtils.isNotNull(begin) && ObjectUtils.isNotNull(end) && !end.after(begin)) {
putFieldError(propertyName, KFSKeyConstants.ERROR_ENDING_DATE_NOT_AFTER_BEGIN);
success = false;
}
return success;
}
/**
* @param <E>
* @param primaryables
* @param elementClass
* @param collectionName
* @param boClass
* @return
*/
protected <E extends Primaryable> boolean checkPrimary(Collection<E> primaryables, Class<E> elementClass, String collectionName, Class<? extends BusinessObject> boClass) {
boolean success = true;
int count = 0;
for (Primaryable p : primaryables) {
if (p.isPrimary()) {
count++;
}
}
if (count != 1) {
success = false;
String elementLabel = SpringContext.getBean(DataDictionaryService.class).getCollectionElementLabel(boClass.getName(), collectionName, elementClass);
switch (count) {
case 0:
putFieldError(collectionName, KFSKeyConstants.ERROR_NO_PRIMARY, elementLabel);
break;
default:
putFieldError(collectionName, KFSKeyConstants.ERROR_MULTIPLE_PRIMARY, elementLabel);
}
}
return success;
}
/**
* @param <T>
* @param projectDirectors
* @param elementClass
* @param collectionName
* @return
*/
protected <T extends CGProjectDirector> boolean checkProjectDirectorsExist(List<T> projectDirectors, Class<T> elementClass, String collectionName) {
boolean success = true;
final String personUserPropertyName = KFSPropertyConstants.PROJECT_DIRECTOR + "." + KFSPropertyConstants.PERSON_USER_IDENTIFIER;
String label = SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(elementClass, personUserPropertyName);
int i = 0;
for (T pd : projectDirectors) {
String propertyName = collectionName + "[" + (i++) + "]." + personUserPropertyName;
String id = pd.getPrincipalId();
if (StringUtils.isBlank(id) || (KimApiServiceLocator.getIdentityService().getPrincipal(id) == null)) {
putFieldError(propertyName, KFSKeyConstants.ERROR_EXISTENCE, label);
success = false;
}
}
return success;
}
/**
*
* @param fundManagers
* @param collectionName
* @return
*/
protected boolean checkFundManagersExist(List<AwardFundManager> fundManagers, String collectionName) {
boolean success = true;
final String personUserPropertyName = KFSPropertyConstants.FUND_MANAGER + "." + KFSPropertyConstants.PERSON_USER_IDENTIFIER;
String label = SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(AwardFundManager.class, personUserPropertyName);
int i = 0;
for (AwardFundManager fm : fundManagers) {
String propertyName = collectionName + "[" + (i++) + "]." + personUserPropertyName;
String id = fm.getPrincipalId();
if (StringUtils.isBlank(id) || (SpringContext.getBean(PersonService.class).getPerson(id) == null)) {
putFieldError(propertyName, KFSKeyConstants.ERROR_EXISTENCE, label);
success = false;
}
}
return success;
}
/**
* @param <T>
* @param projectDirectors
* @param elementClass
* @param collectionName
* @return
*/
protected <T extends CGProjectDirector> boolean checkProjectDirectorsAreDirectors(List<T> projectDirectors, Class<T> elementClass, String collectionName) {
boolean success = true;
final String personUserPropertyName = KFSPropertyConstants.PROJECT_DIRECTOR + "." + KFSPropertyConstants.PERSON_USER_IDENTIFIER;
String label = SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(elementClass, personUserPropertyName);
RoleService roleService = KimApiServiceLocator.getRoleService();
List<String> roleId = new ArrayList<String>();
roleId.add(roleService.getRoleIdByNamespaceCodeAndName(KFSConstants.ParameterNamespaces.KFS, KFSConstants.SysKimApiConstants.CONTRACTS_AND_GRANTS_PROJECT_DIRECTOR));
int i = 0;
for (T pd : projectDirectors) {
String propertyName = collectionName + "[" + (i++) + "]." + personUserPropertyName;
String id = pd.getProjectDirector().getPrincipalId();
if (!roleService.principalHasRole(id, roleId, null)) {
putFieldError(propertyName, KFSKeyConstants.ERROR_NOT_A_PROJECT_DIRECTOR, id);
success = false;
}
}
return success;
}
/**
* This method takes in a collection of {@link ProjectDirector}s and reviews them to see if any have invalid states for being
* added to a {@link Proposal}. An example would be a status code of "D" which means "Deceased". Project Directors with a status
* of "D" cannot be added to a {@link Proposal} or {@link Award}.
*
* @param projectDirectors Collection of project directors to be reviewed.
* @param elementClass Type of object that the collection belongs to.
* @param propertyName Name of field that error will be attached to.
* @return True if all the project directors have valid statuses, false otherwise.
*/
protected <T extends CGProjectDirector> boolean checkProjectDirectorsStatuses(List<T> projectDirectors, Class<T> elementClass, String propertyName) {
boolean success = true;
for (T pd : projectDirectors) {
String pdEmplStatusCode = pd.getProjectDirector().getEmployeeStatusCode();
if (StringUtils.isBlank(pdEmplStatusCode) || Arrays.asList(PROJECT_DIRECTOR_INVALID_STATUSES).contains(pdEmplStatusCode)) {
String pdEmplStatusName = "INVALID STATUS CODE " + pdEmplStatusCode;
if ( StringUtils.isNotBlank(pdEmplStatusCode) ) {
CodedAttribute empStatus = KimApiServiceLocator.getIdentityService().getEmploymentStatus(pdEmplStatusCode);
if ( empStatus != null ) {
pdEmplStatusName = empStatus.getName();
}
}
String[] errors = { pd.getProjectDirector().getName(), pdEmplStatusCode + " - " + pdEmplStatusName };
putFieldError(propertyName, KFSKeyConstants.ERROR_INVALID_PROJECT_DIRECTOR_STATUS, errors);
success = false;
}
}
return success;
}
/**
* This method checks to see if the two agency values passed in are the same {@link Agency}. The agency for a C&G document
* cannot be the same as the Federal Pass Through Agency for that same document.
*
* @param agency
* @param federalPassThroughAgency
* @param agencyPropertyName
* @return True if the agencies are not the same, false otherwise.
*/
protected boolean checkAgencyNotEqualToFederalPassThroughAgency(Agency agency, Agency federalPassThroughAgency, String agencyPropertyName, String fedPassThroughAgencyPropertyName) {
boolean success = true;
if (ObjectUtils.isNotNull(agency) && ObjectUtils.isNotNull(federalPassThroughAgency) && ObjectUtils.isNotNull(agency.getAgencyNumber()) && ObjectUtils.isNotNull(federalPassThroughAgency.getAgencyNumber()) && agency.equals(federalPassThroughAgency)) {
putFieldError(agencyPropertyName, KFSKeyConstants.ERROR_AGENCY_EQUALS_FEDERAL_PASS_THROUGH_AGENCY);
putFieldError(fedPassThroughAgencyPropertyName, KFSKeyConstants.ERROR_FEDERAL_PASS_THROUGH_AGENCY_EQUALS_AGENCY);
success = false;
}
return success;
}
/**
* Checks if the required federal pass through fields are filled in if the federal pass through indicator is yes.
*
* @return True if all the necessary rules regarding the federal pass through agency input fields are met, false otherwise.
*/
protected boolean checkFederalPassThrough(boolean federalPassThroughIndicator, Agency primaryAgency, String federalPassThroughAgencyNumber, Class propertyClass, String federalPassThroughIndicatorFieldName) {
boolean success = true;
// check if primary agency is federal
boolean primaryAgencyIsFederal = false;
if (ObjectUtils.isNotNull(primaryAgency)) {
primaryAgencyIsFederal = AGENCY_TYPE_CODE_FEDERAL.equalsIgnoreCase(primaryAgency.getAgencyTypeCode());
}
String indicatorLabel = SpringContext.getBean(DataDictionaryService.class).getAttributeErrorLabel(propertyClass, federalPassThroughIndicatorFieldName);
String agencyLabel = SpringContext.getBean(DataDictionaryService.class).getAttributeErrorLabel(propertyClass, KFSPropertyConstants.FEDERAL_PASS_THROUGH_AGENCY_NUMBER);
if (primaryAgencyIsFederal) {
if (federalPassThroughIndicator) {
// fpt indicator should not be checked if primary agency is federal
putFieldError(federalPassThroughIndicatorFieldName, KFSKeyConstants.ERROR_PRIMARY_AGENCY_IS_FEDERAL_AND_FPT_INDICATOR_IS_CHECKED, new String[] { primaryAgency.getAgencyNumber(), AGENCY_TYPE_CODE_FEDERAL });
success = false;
}
if (!StringUtils.isBlank(federalPassThroughAgencyNumber)) {
// fpt agency number should be blank if primary agency is federal
putFieldError(KFSPropertyConstants.FEDERAL_PASS_THROUGH_AGENCY_NUMBER, KFSKeyConstants.ERROR_PRIMARY_AGENCY_IS_FEDERAL_AND_FPT_AGENCY_IS_NOT_BLANK, new String[] { primaryAgency.getAgencyNumber(), AGENCY_TYPE_CODE_FEDERAL });
success = false;
}
}
else {
if (federalPassThroughIndicator && StringUtils.isBlank(federalPassThroughAgencyNumber)) {
// fpt agency number is required if fpt indicator is checked
putFieldError(KFSPropertyConstants.FEDERAL_PASS_THROUGH_AGENCY_NUMBER, KFSKeyConstants.ERROR_FPT_AGENCY_NUMBER_REQUIRED);
success = false;
}
else if (!federalPassThroughIndicator && !StringUtils.isBlank(federalPassThroughAgencyNumber)) {
// fpt agency number should be blank if fpt indicator is not checked
putFieldError(KFSPropertyConstants.FEDERAL_PASS_THROUGH_AGENCY_NUMBER, KFSKeyConstants.ERROR_FPT_AGENCY_NUMBER_NOT_BLANK);
success = false;
}
}
return success;
}
}