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