001 /*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.kfs.module.cg.document.validation.impl;
017
018 import java.sql.Date;
019 import java.util.ArrayList;
020 import java.util.Arrays;
021 import java.util.Collection;
022 import java.util.Collections;
023 import java.util.HashMap;
024 import java.util.List;
025
026 import org.apache.commons.lang.StringUtils;
027 import org.kuali.kfs.module.cg.businessobject.Agency;
028 import org.kuali.kfs.module.cg.businessobject.CGProjectDirector;
029 import org.kuali.kfs.module.cg.businessobject.Primaryable;
030 import org.kuali.kfs.sys.KFSConstants;
031 import org.kuali.kfs.sys.KFSKeyConstants;
032 import org.kuali.kfs.sys.KFSPropertyConstants;
033 import org.kuali.kfs.sys.context.SpringContext;
034 import org.kuali.rice.kim.bo.reference.impl.EmploymentStatusImpl;
035 import org.kuali.rice.kim.service.PersonService;
036 import org.kuali.rice.kim.service.RoleManagementService;
037 import org.kuali.rice.kns.bo.BusinessObject;
038 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
039 import org.kuali.rice.kns.service.DataDictionaryService;
040 import org.kuali.rice.kns.util.ObjectUtils;
041
042 /**
043 * Rules for the Proposal/Award maintenance document.
044 */
045 public class CGMaintenanceDocumentRuleBase extends MaintenanceDocumentRuleBase {
046
047 protected static final String PROJECT_DIRECTOR_DECEASED = "D";
048 protected static final String[] PROJECT_DIRECTOR_INVALID_STATUSES = { PROJECT_DIRECTOR_DECEASED };
049
050 protected static final String AGENCY_TYPE_CODE_FEDERAL = "F";
051
052 /**
053 * Checks to see if the end date is after the begin date
054 *
055 * @param begin
056 * @param end
057 * @param propertyName
058 * @return true if end is after begin, false otherwise
059 */
060 protected boolean checkEndAfterBegin(Date begin, Date end, String propertyName) {
061 boolean success = true;
062 if (ObjectUtils.isNotNull(begin) && ObjectUtils.isNotNull(end) && !end.after(begin)) {
063 putFieldError(propertyName, KFSKeyConstants.ERROR_ENDING_DATE_NOT_AFTER_BEGIN);
064 success = false;
065 }
066 return success;
067 }
068
069 /**
070 * @param <E>
071 * @param primaryables
072 * @param elementClass
073 * @param collectionName
074 * @param boClass
075 * @return
076 */
077 protected <E extends Primaryable> boolean checkPrimary(Collection<E> primaryables, Class<E> elementClass, String collectionName, Class<? extends BusinessObject> boClass) {
078 boolean success = true;
079 int count = 0;
080 for (Primaryable p : primaryables) {
081 if (p.isPrimary()) {
082 count++;
083 }
084 }
085 if (count != 1) {
086 success = false;
087 String elementLabel = SpringContext.getBean(DataDictionaryService.class).getCollectionElementLabel(boClass.getName(), collectionName, elementClass);
088 switch (count) {
089 case 0:
090 putFieldError(collectionName, KFSKeyConstants.ERROR_NO_PRIMARY, elementLabel);
091 break;
092 default:
093 putFieldError(collectionName, KFSKeyConstants.ERROR_MULTIPLE_PRIMARY, elementLabel);
094 }
095
096 }
097 return success;
098 }
099
100 /**
101 * @param <T>
102 * @param projectDirectors
103 * @param elementClass
104 * @param collectionName
105 * @return
106 */
107 protected <T extends CGProjectDirector> boolean checkProjectDirectorsExist(List<T> projectDirectors, Class<T> elementClass, String collectionName) {
108 boolean success = true;
109 final String personUserPropertyName = KFSPropertyConstants.PROJECT_DIRECTOR + "." + KFSPropertyConstants.PERSON_USER_IDENTIFIER;
110 String label = SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(elementClass, personUserPropertyName);
111 int i = 0;
112 for (T pd : projectDirectors) {
113 String propertyName = collectionName + "[" + (i++) + "]." + personUserPropertyName;
114 String id = pd.getPrincipalId();
115 if (StringUtils.isBlank(id) || (SpringContext.getBean(PersonService.class).getPerson(id) == null)) {
116 putFieldError(propertyName, KFSKeyConstants.ERROR_EXISTENCE, label);
117 success = false;
118 }
119 }
120 return success;
121 }
122
123 /**
124 * @param <T>
125 * @param projectDirectors
126 * @param elementClass
127 * @param collectionName
128 * @return
129 */
130 protected <T extends CGProjectDirector> boolean checkProjectDirectorsAreDirectors(List<T> projectDirectors, Class<T> elementClass, String collectionName) {
131 boolean success = true;
132 final String personUserPropertyName = KFSPropertyConstants.PROJECT_DIRECTOR + "." + KFSPropertyConstants.PERSON_USER_IDENTIFIER;
133 String label = SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(elementClass, personUserPropertyName);
134 RoleManagementService roleService = SpringContext.getBean(RoleManagementService.class);
135
136 List<String> roleId = new ArrayList<String>();
137 roleId.add(roleService.getRoleIdByName(KFSConstants.ParameterNamespaces.KFS, KFSConstants.SysKimConstants.CONTRACTS_AND_GRANTS_PROJECT_DIRECTOR));
138
139 int i = 0;
140 for (T pd : projectDirectors) {
141 String propertyName = collectionName + "[" + (i++) + "]." + personUserPropertyName;
142 String id = pd.getProjectDirector().getPrincipalId();
143 if (!roleService.principalHasRole(id, roleId, null)) {
144 putFieldError(propertyName, KFSKeyConstants.ERROR_NOT_A_PROJECT_DIRECTOR, id);
145 success = false;
146 }
147 }
148 return success;
149 }
150
151
152 /**
153 * This method takes in a collection of {@link ProjectDirector}s and reviews them to see if any have invalid states for being
154 * added to a {@link Proposal}. An example would be a status code of "D" which means "Deceased". Project Directors with a
155 * status of "D" cannot be added to a {@link Proposal} or {@link Award}.
156 *
157 * @param projectDirectors Collection of project directors to be reviewed.
158 * @param elementClass Type of object that the collection belongs to.
159 * @param propertyName Name of field that error will be attached to.
160 * @return True if all the project directors have valid statuses, false otherwise.
161 */
162 protected <T extends CGProjectDirector> boolean checkProjectDirectorsStatuses(List<T> projectDirectors, Class<T> elementClass, String propertyName) {
163 boolean success = true;
164 final String personUserPropertyName = KFSPropertyConstants.PROJECT_DIRECTOR + "." + KFSPropertyConstants.PERSON_USER_IDENTIFIER;
165 String label = SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(elementClass, personUserPropertyName);
166 for (T pd : projectDirectors) {
167 String pdEmplStatusCode = pd.getProjectDirector().getEmployeeStatusCode();
168 HashMap<String, String> criteria = new HashMap<String, String>();
169 criteria.put( "code", pdEmplStatusCode );
170 if (StringUtils.isBlank(pdEmplStatusCode) || Arrays.asList(PROJECT_DIRECTOR_INVALID_STATUSES).contains(pdEmplStatusCode)) {
171 EmploymentStatusImpl empStatus = (EmploymentStatusImpl)getBoService().findByPrimaryKey(EmploymentStatusImpl.class, criteria);
172 String pdEmplStatusName = (empStatus != null)?empStatus.getName():"INVALID STATUS CODE " + pdEmplStatusCode;
173 String[] errors = { pd.getProjectDirector().getName(), pdEmplStatusCode + " - " + pdEmplStatusName };
174 putFieldError(propertyName, KFSKeyConstants.ERROR_INVALID_PROJECT_DIRECTOR_STATUS, errors);
175 success = false;
176 }
177 }
178 return success;
179 }
180
181 /**
182 * This method checks to see if the two agency values passed in are the same {@link Agency}. The agency for a C&G document
183 * cannot be the same as the Federal Pass Through Agency for that same document.
184 *
185 * @param agency
186 * @param federalPassThroughAgency
187 * @param agencyPropertyName
188 * @return True if the agencies are not the same, false otherwise.
189 */
190 protected boolean checkAgencyNotEqualToFederalPassThroughAgency(Agency agency, Agency federalPassThroughAgency, String agencyPropertyName, String fedPassThroughAgencyPropertyName) {
191 boolean success = true;
192 if (ObjectUtils.isNotNull(agency) && ObjectUtils.isNotNull(federalPassThroughAgency) && agency.equals(federalPassThroughAgency)) {
193 putFieldError(agencyPropertyName, KFSKeyConstants.ERROR_AGENCY_EQUALS_FEDERAL_PASS_THROUGH_AGENCY);
194 putFieldError(fedPassThroughAgencyPropertyName, KFSKeyConstants.ERROR_FEDERAL_PASS_THROUGH_AGENCY_EQUALS_AGENCY);
195 success = false;
196 }
197 return success;
198 }
199
200 /**
201 * Checks if the required federal pass through fields are filled in if the federal pass through indicator is yes.
202 *
203 * @return True if all the necessary rules regarding the federal pass through agency input fields are met, false otherwise.
204 */
205 protected boolean checkFederalPassThrough(boolean federalPassThroughIndicator, Agency primaryAgency, String federalPassThroughAgencyNumber, Class propertyClass, String federalPassThroughIndicatorFieldName) {
206 boolean success = true;
207
208 // check if primary agency is federal
209 boolean primaryAgencyIsFederal = false;
210
211 if (ObjectUtils.isNotNull(primaryAgency)) {
212 primaryAgencyIsFederal = AGENCY_TYPE_CODE_FEDERAL.equalsIgnoreCase(primaryAgency.getAgencyTypeCode());
213 }
214
215 String indicatorLabel = SpringContext.getBean(DataDictionaryService.class).getAttributeErrorLabel(propertyClass, federalPassThroughIndicatorFieldName);
216 String agencyLabel = SpringContext.getBean(DataDictionaryService.class).getAttributeErrorLabel(propertyClass, KFSPropertyConstants.FEDERAL_PASS_THROUGH_AGENCY_NUMBER);
217
218 if (primaryAgencyIsFederal) {
219 if (federalPassThroughIndicator) {
220 // fpt indicator should not be checked if primary agency is federal
221 putFieldError(federalPassThroughIndicatorFieldName, KFSKeyConstants.ERROR_PRIMARY_AGENCY_IS_FEDERAL_AND_FPT_INDICATOR_IS_CHECKED, new String[] { primaryAgency.getAgencyNumber(), AGENCY_TYPE_CODE_FEDERAL });
222 success = false;
223 }
224 if (!StringUtils.isBlank(federalPassThroughAgencyNumber)) {
225 // fpt agency number should be blank if primary agency is federal
226 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 });
227 success = false;
228 }
229 }
230 else {
231 if (federalPassThroughIndicator && StringUtils.isBlank(federalPassThroughAgencyNumber)) {
232 // fpt agency number is required if fpt indicator is checked
233 putFieldError(KFSPropertyConstants.FEDERAL_PASS_THROUGH_AGENCY_NUMBER, KFSKeyConstants.ERROR_FPT_AGENCY_NUMBER_REQUIRED);
234 success = false;
235 }
236 else if (!federalPassThroughIndicator && !StringUtils.isBlank(federalPassThroughAgencyNumber)) {
237 // fpt agency number should be blank if fpt indicator is not checked
238 putFieldError(KFSPropertyConstants.FEDERAL_PASS_THROUGH_AGENCY_NUMBER, KFSKeyConstants.ERROR_FPT_AGENCY_NUMBER_NOT_BLANK);
239 success = false;
240 }
241 }
242
243 return success;
244 }
245
246 }
247