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.coa.document.validation.impl;
017
018 import java.util.ArrayList;
019 import java.util.Collection;
020 import java.util.HashMap;
021 import java.util.List;
022 import java.util.Map;
023 import java.util.Properties;
024 import java.util.Set;
025
026 import org.apache.commons.lang.StringUtils;
027 import org.kuali.kfs.coa.businessobject.IndirectCostRecoveryExclusionAccount;
028 import org.kuali.kfs.coa.businessobject.ObjectCode;
029 import org.kuali.kfs.coa.businessobject.ObjectCodeGlobal;
030 import org.kuali.kfs.coa.businessobject.ObjectCodeGlobalDetail;
031 import org.kuali.kfs.coa.businessobject.ObjectLevel;
032 import org.kuali.kfs.coa.businessobject.OffsetDefinition;
033 import org.kuali.kfs.coa.service.ObjectCodeService;
034 import org.kuali.kfs.coa.service.ObjectLevelService;
035 import org.kuali.kfs.sys.KFSConstants;
036 import org.kuali.kfs.sys.KFSKeyConstants;
037 import org.kuali.kfs.sys.KFSPropertyConstants;
038 import org.kuali.kfs.sys.context.SpringContext;
039 import org.kuali.kfs.sys.service.UniversityDateService;
040 import org.kuali.rice.kns.bo.BusinessObject;
041 import org.kuali.rice.kns.bo.GlobalBusinessObject;
042 import org.kuali.rice.kns.bo.PersistableBusinessObject;
043 import org.kuali.rice.kns.datadictionary.InactivationBlockingMetadata;
044 import org.kuali.rice.kns.document.MaintenanceDocument;
045 import org.kuali.rice.kns.maintenance.Maintainable;
046 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
047 import org.kuali.rice.kns.service.BusinessObjectService;
048 import org.kuali.rice.kns.service.InactivationBlockingDetectionService;
049 import org.kuali.rice.kns.service.KNSServiceLocator;
050 import org.kuali.rice.kns.util.GlobalVariables;
051 import org.kuali.rice.kns.util.KNSConstants;
052 import org.kuali.rice.kns.util.ObjectUtils;
053 import org.kuali.rice.kns.util.UrlFactory;
054
055 /**
056 * This class represents the business rules for the maintenance of {@link ObjectCodeGlobal} business objects
057 */
058 public class ObjectCodeGlobalRule extends MaintenanceDocumentRuleBase {
059 protected ObjectCodeGlobal objectCodeGlobal;
060 protected ObjectCodeService objectCodeService;
061 protected ObjectLevelService objectLevelService;
062
063 public ObjectCodeGlobalRule() {
064 super();
065 setObjectCodeService(SpringContext.getBean(ObjectCodeService.class));
066 setObjectLevelService(SpringContext.getBean(ObjectLevelService.class));
067 }
068
069
070 /**
071 * This method sets the convenience objects like objectCodeGlobal, so you have short and easy handles to the new and
072 * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load
073 * all sub-objects from the DB by their primary keys, if available.
074 *
075 * @param document - the maintenanceDocument being evaluated
076 */
077 @Override
078 public void setupConvenienceObjects() {
079
080 // setup ObjectCodeGlobal convenience objects,
081 // make sure all possible sub-objects are populated
082 objectCodeGlobal = (ObjectCodeGlobal) super.getNewBo();
083
084 // forces refreshes on all the sub-objects in the lists
085 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
086 objectCodeGlobalDetail.refreshNonUpdateableReferences();
087 }
088 }
089
090 /**
091 * This performs rules checks on document approve
092 * <ul>
093 * <li>{@link ObjectCodeGlobalRule#checkSimpleRulesAllLines()}</li>
094 * </ul>
095 * This rule fails on business rule failures
096 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
097 */
098 @Override
099 protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
100 boolean success = true;
101 setupConvenienceObjects();
102 // check simple rules
103 success &= checkSimpleRulesAllLines();
104 return success;
105 }
106
107 /**
108 * This performs rules checks on document route
109 * <ul>
110 * <li>{@link ObjectCodeGlobalRule#checkSimpleRulesAllLines()}</li>
111 * </ul>
112 * This rule fails on business rule failures
113 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
114 */
115 @Override
116 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
117 boolean success = true;
118 setupConvenienceObjects();
119 // check simple rules
120 success &= checkSimpleRulesAllLines();
121 return success;
122 }
123
124
125 @Override
126 protected boolean processInactivationBlockChecking(MaintenanceDocument maintenanceDocument) {
127 boolean success = true;
128 if (!objectCodeGlobal.isFinancialObjectActiveIndicator()) {
129 // we can only inactivate if the new active status will be false, now check whether the object codes on the document exist and are currently true
130 List<ObjectCodeGlobalDetail> objectCodeGlobalDetails = objectCodeGlobal.getObjectCodeGlobalDetails();
131 for (int i = 0; i < objectCodeGlobalDetails.size(); i++) {
132 ObjectCodeGlobalDetail objectCodeGlobalDetail = objectCodeGlobalDetails.get(i);
133 // get current object code from the DB
134 ObjectCode objectCode = objectCodeService.getByPrimaryId(objectCodeGlobalDetail.getUniversityFiscalYear(), objectCodeGlobalDetail.getChartOfAccountsCode(), objectCodeGlobal.getFinancialObjectCode());
135 if (ObjectUtils.isNotNull(objectCode)) {
136 if (objectCode.isActive()) {
137 // now we know that the document intends to inactivate this object code... check to see whether a record blocks it
138 success &= processInactivationBlockChecking(maintenanceDocument.getNewMaintainableObject(), objectCode, i);
139 }
140 }
141 }
142 }
143 return success;
144 }
145
146 protected boolean processInactivationBlockChecking(Maintainable maintainable, ObjectCode objectCode, int index) {
147 Set<InactivationBlockingMetadata> inactivationBlockingMetadatas = ddService.getAllInactivationBlockingDefinitions(ObjectCode.class);
148 for (InactivationBlockingMetadata inactivationBlockingMetadata : inactivationBlockingMetadatas) {
149 String inactivationBlockingDetectionServiceBeanName = inactivationBlockingMetadata.getInactivationBlockingDetectionServiceBeanName();
150 if (StringUtils.isBlank(inactivationBlockingDetectionServiceBeanName)) {
151 inactivationBlockingDetectionServiceBeanName = KNSServiceLocator.DEFAULT_INACTIVATION_BLOCKING_DETECTION_SERVICE;
152 }
153 InactivationBlockingDetectionService inactivationBlockingDetectionService = KNSServiceLocator.getInactivationBlockingDetectionService(inactivationBlockingDetectionServiceBeanName);
154
155 Collection<BusinessObject> blockingBusinessObjects = inactivationBlockingDetectionService.listAllBlockerRecords(objectCode, inactivationBlockingMetadata);
156 blockingBusinessObjects = addAdditionalBlockingBusinessObjects(blockingBusinessObjects, objectCode);
157
158 if (blockingBusinessObjects != null && !blockingBusinessObjects.isEmpty()) {
159 final List<PersistableBusinessObject> persistingChanges = ((GlobalBusinessObject)maintainable.getBusinessObject()).generateGlobalChangesToPersist();
160 if (!isOnlyPersistingChangesInBlockingBusinessObjects(blockingBusinessObjects, persistingChanges)) {
161 putInactivationBlockingErrorOnPage(objectCode, inactivationBlockingMetadata, index);
162 return false;
163 }
164 }
165 }
166 return true;
167 }
168
169 protected void putInactivationBlockingErrorOnPage(ObjectCode objectCode, InactivationBlockingMetadata inactivationBlockingMetadata, int index) {
170 String objectCodeSummaryString = objectCode.getUniversityFiscalYear() + " - " + objectCode.getChartOfAccountsCode() + " - " + objectCode.getFinancialObjectCode();
171
172 Properties parameters = new Properties();
173 parameters.put(KNSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, inactivationBlockingMetadata.getBlockedBusinessObjectClass().getName());
174 parameters.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, KNSConstants.METHOD_DISPLAY_ALL_INACTIVATION_BLOCKERS);
175 parameters.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, objectCode.getUniversityFiscalYear().toString());
176 parameters.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, objectCode.getChartOfAccountsCode());
177 parameters.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, objectCode.getFinancialObjectCode());
178 String blockingUrl = UrlFactory.parameterizeUrl(KNSConstants.DISPLAY_ALL_INACTIVATION_BLOCKERS_ACTION, parameters);
179
180 String errorPropertyPath = KFSConstants.MAINTENANCE_NEW_MAINTAINABLE + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + index + "]." + KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE;
181
182 // post an error about the locked document
183 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPropertyPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INACTIVATION_BLOCKING, objectCodeSummaryString, blockingUrl);
184 }
185
186 /**
187 * Determines if all of the given blocking business objects are among the persisting changes
188 * @param blockingBusinessObjects the Collection of blocking business objects
189 * @param persistingChanges the List of Object Codes which will be persisted by this document
190 * @return true if all the blocking business objects are persisting changes
191 */
192 protected boolean isOnlyPersistingChangesInBlockingBusinessObjects(Collection<BusinessObject> blockingBusinessObjects, List<PersistableBusinessObject> persistingChanges) {
193 for (BusinessObject bo : blockingBusinessObjects) {
194 if (bo instanceof ObjectCode) {
195 if (!isObjectCodeInPersistingChanges(persistingChanges, (ObjectCode)bo)) return false;
196 } else {
197 return false;
198 }
199 }
200 return true;
201 }
202
203 /**
204 * Determines if the given object code is within the list of persisting changes
205 * @param persistingChanges the changes to persist
206 * @param objectCode the blocking object code to look for in the persisting changes
207 * @return true if the object code was found in the list of persisting changes, false otherwise
208 */
209 protected boolean isObjectCodeInPersistingChanges(List<PersistableBusinessObject> persistingChanges, ObjectCode objectCode) {
210 for (PersistableBusinessObject persistingObjectCodeAsObject : persistingChanges) {
211 if (isEqualObjectCode(objectCode, (ObjectCode)persistingObjectCodeAsObject)) return true;
212 }
213 return false;
214 }
215
216 /**
217 * Determines if the two given object codes are roughly equal
218 * @param castor an object code
219 * @param pollux another, though perhaps very similar, object code
220 * @return true if the two object codes share primary key values, false otherwise
221 */
222 protected boolean isEqualObjectCode(ObjectCode castor, ObjectCode pollux) {
223 return ObjectUtils.nullSafeEquals(castor.getUniversityFiscalYear(), pollux.getUniversityFiscalYear()) && ObjectUtils.nullSafeEquals(castor.getChartOfAccountsCode(), pollux.getChartOfAccountsCode()) && ObjectUtils.nullSafeEquals(castor.getFinancialObjectCode(), pollux.getFinancialObjectCode());
224 }
225
226 /**
227 * Retrieves any additional blocking objects not handled by the inactivation framework
228 * @param blockingBusinessObjects the current list of blocking business objects
229 * @param objectCode the object code to find additional blocking objects for
230 * @return the perhaps fuller Collection of blocking business objects
231 */
232 protected Collection<BusinessObject> addAdditionalBlockingBusinessObjects(Collection<BusinessObject> blockingBusinessObjects, ObjectCode objectCode) {
233 List<BusinessObject> additionalBlockingObjects = new ArrayList<BusinessObject>();
234 retrieveBlockingOffsetDefinitions(objectCode, additionalBlockingObjects);
235 retrieveBlockingIndirectCostRecoveryExclusionAccounts(objectCode, additionalBlockingObjects);
236 if (!additionalBlockingObjects.isEmpty()) {
237 additionalBlockingObjects.addAll(blockingBusinessObjects);
238 return additionalBlockingObjects;
239 }
240 return blockingBusinessObjects;
241 }
242
243 /**
244 * Retrieves all Offset Definitions blocking the given object code and puts them in the List of additional blocking objects
245 * @param objectCode the object code to find additional blocking objects for
246 * @param additionalBlockingObjects the List of additional blocking objects to populate
247 */
248 protected void retrieveBlockingOffsetDefinitions(ObjectCode objectCode, List<BusinessObject> additionalBlockingObjects) {
249 final BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
250
251 Map<String, Object> keys = new HashMap<String, Object>();
252 keys.put("universityFiscalYear", objectCode.getUniversityFiscalYear());
253 keys.put("chartOfAccountsCode", objectCode.getChartOfAccountsCode());
254 keys.put("financialObjectCode", objectCode.getFinancialObjectCode());
255
256 Collection<BusinessObject> offsetDefinitions = (Collection<BusinessObject>)businessObjectService.findMatching(OffsetDefinition.class, keys);
257 if (offsetDefinitions != null && !offsetDefinitions.isEmpty()) {
258 additionalBlockingObjects.addAll(offsetDefinitions);
259 }
260 }
261
262 /**
263 * Retrieves all Indirect Cost Recovery Exclusion by Account records blocking the given object code and puts them in the List of additional blocking objects
264 * @param objectCode the object code to find additional blocking objects for
265 * @param additionalBlockingObjects the List of additional blocking objects to populate
266 */
267 protected void retrieveBlockingIndirectCostRecoveryExclusionAccounts(ObjectCode objectCode, List<BusinessObject> additionalBlockingObjects) {
268 final UniversityDateService universityDateService = SpringContext.getBean(UniversityDateService.class);
269 if (objectCode.getUniversityFiscalYear() != null && objectCode.getUniversityFiscalYear().equals(universityDateService.getCurrentFiscalYear())) {
270 final BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
271
272 Map<String, Object> keys = new HashMap<String, Object>();
273 keys.put("financialObjectChartOfAccountCode", objectCode.getChartOfAccountsCode());
274 keys.put("financialObjectCode", objectCode.getFinancialObjectCode());
275
276 Collection<BusinessObject> icrExclusionAccounts = (Collection<BusinessObject>)businessObjectService.findMatching(IndirectCostRecoveryExclusionAccount.class, keys);
277 if (icrExclusionAccounts != null && !icrExclusionAccounts.isEmpty()) {
278 additionalBlockingObjects.addAll(icrExclusionAccounts);
279 }
280 }
281 }
282
283 /**
284 * This performs rules checks on document save
285 * <ul>
286 * <li>{@link ObjectCodeGlobalRule#checkSimpleRulesAllLines()}</li>
287 * </ul>
288 * This rule does not fail on business rule failures
289 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
290 */
291 @Override
292 protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
293 setupConvenienceObjects();
294 // check simple rules
295 checkSimpleRulesAllLines();
296
297 return true;
298 }
299
300 /**
301 * This method checks to make sure that each new {@link ObjectCodeGlobalDetail} has:
302 * <ul>
303 * <li>valid chart of accounts code</li>
304 * <li>valid fiscal year</li>
305 * <li>unique identifiers (not currently implemented)</li>
306 * </ul>
307 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument, java.lang.String, org.kuali.rice.kns.bo.PersistableBusinessObject)
308 */
309 @Override
310 public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject bo) {
311 boolean success = true;
312 if (bo instanceof ObjectCodeGlobalDetail) {
313 ObjectCodeGlobalDetail detail = (ObjectCodeGlobalDetail) bo;
314 if (!checkEmptyValue(detail.getChartOfAccountsCode())) {
315 // put an error about chart code
316 GlobalVariables.getMessageMap().putError("chartOfAccountsCode", KFSKeyConstants.ERROR_REQUIRED, "Chart of Accounts Code");
317 success &= false;
318 }
319 if (!checkEmptyValue(detail.getUniversityFiscalYear())) {
320 // put an error about fiscal year
321 GlobalVariables.getMessageMap().putError("universityFiscalYear", KFSKeyConstants.ERROR_REQUIRED, "University Fiscal Year");
322 success &= false;
323 }
324 if (!checkUniqueIdentifiers(detail)) {
325 // TODO: put an error about unique identifier fields must not exist more than once.
326 success &= false;
327 }
328 // both keys are present and satisfy the unique identifiers requirement, go ahead and proces the rest of the rules
329 if (success) {
330 success &= checkObjectCodeDetails(detail);
331 }
332
333 }
334 return success;
335 }
336
337 /**
338 *
339 * This method (will)put an error about unique identifier fields must not exist more than once.
340 * @param dtl
341 * @return true (not currently implemented fully)
342 */
343 protected boolean checkUniqueIdentifiers(ObjectCodeGlobalDetail dtl) {
344 boolean success = true;
345 return success;
346
347 }
348
349 /**
350 *
351 * This checks the following conditions:
352 * <ul>
353 * <li>{@link ObjectCodeGlobalRule#checkObjectLevelCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} </li>
354 * <li>{@link ObjectCodeGlobalRule#checkNextYearObjectCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} </li>
355 * <li>{@link ObjectCodeGlobalRule#checkReportsToObjectCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)}</li>
356 * </ul>
357 * @param dtl
358 * @return true if sub-rules succeed
359 */
360 public boolean checkObjectCodeDetails(ObjectCodeGlobalDetail dtl) {
361 boolean success = true;
362 int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount();
363 getDictionaryValidationService().validateBusinessObject(dtl);
364 dtl.refreshNonUpdateableReferences();
365 // here is where we need our checks for level code nd next year object code
366 success &= checkObjectLevelCode(objectCodeGlobal, dtl, 0, true);
367 success &= checkNextYearObjectCode(objectCodeGlobal, dtl, 0, true);
368 success &= checkReportsToObjectCode(objectCodeGlobal, dtl, 0, true);
369 success &= GlobalVariables.getMessageMap().getErrorCount() == originalErrorCount;
370
371 return success;
372 }
373
374 /**
375 * This method checks that the reports to object code input on the top level of the global document is valid for a given chart's
376 * reportToChart in the detail section
377 *
378 * @param dtl
379 * @return true if the reports to object is valid for the given reports to chart
380 */
381 protected boolean checkReportsToObjectCode(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail dtl, int lineNum, boolean add) {
382 boolean success = true;
383 String errorPath = KFSConstants.EMPTY_STRING;
384 if (checkEmptyValue(objectCodeGlobal.getReportsToFinancialObjectCode())) {
385 // objectCodeGlobal.refreshReferenceObject("reportsToFinancialObject");
386 String reportsToObjectCode = objectCodeGlobal.getReportsToFinancialObjectCode();
387 String reportsToChartCode = dtl.getChartOfAccounts().getReportsToChartOfAccountsCode();
388 Integer fiscalYear = dtl.getUniversityFiscalYear();
389
390 // verify that this combination exists in the db
391 ObjectCode objCode = objectCodeService.getByPrimaryId(fiscalYear, reportsToChartCode, reportsToObjectCode);
392 if (ObjectUtils.isNull(objCode)) {
393 success &= false;
394 String[] errorParameters = { reportsToObjectCode, reportsToChartCode, fiscalYear.toString() };
395 if (add) {
396 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "chartOfAccountsCode";
397 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_RPTS_TO_OBJ_CODE, errorParameters);
398 }
399 else {
400 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
401 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_RPTS_TO_OBJ_CODE, errorParameters);
402 }
403 }
404 return success;
405
406 }
407 else {
408 GlobalVariables.getMessageMap().putError("reportsToFinancialObjectCode", KFSKeyConstants.ERROR_REQUIRED, "Reports to Object Code");
409 success &= false;
410 }
411
412 return success;
413 }
414
415
416 /**
417 * This method checks that the next year object code specified in the change document is a valid object code for a given chart
418 * and year
419 *
420 * @param dtl
421 * @return false if this object code doesn't exist in the next fiscal year
422 */
423 protected boolean checkNextYearObjectCode(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail dtl, int lineNum, boolean add) {
424 boolean success = true;
425 String errorPath = KFSConstants.EMPTY_STRING;
426 // first check to see if the Next Year object code was filled in
427 if (checkEmptyValue(objectCodeGlobal.getNextYearFinancialObjectCode())) {
428 // then this value must also exist as a regular financial object code currently
429 ObjectCode objCode = objectCodeService.getByPrimaryId(dtl.getUniversityFiscalYear(), dtl.getChartOfAccountsCode(), objectCodeGlobal.getNextYearFinancialObjectCode());
430 if (ObjectUtils.isNull(objCode)) {
431 success &= false;
432 String[] errorParameters = { objectCodeGlobal.getNextYearFinancialObjectCode(), dtl.getChartOfAccountsCode(), dtl.getUniversityFiscalYear().toString() };
433 if (add) {
434 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "chartOfAccountsCode";
435 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_NEXT_YEAR_OBJ_CODE, errorParameters);
436 }
437 else {
438 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
439 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_NEXT_YEAR_OBJ_CODE, errorParameters);
440 }
441 }
442 return success;
443 }
444
445 return success;
446 }
447
448 /**
449 * This method checks that the object level code from the object code change document actually exists for the chart object
450 * specified in the detail
451 *
452 * @param dtl
453 * @return false if object level doesn't exist for the chart, and level code filled in
454 */
455 protected boolean checkObjectLevelCode(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail dtl, int lineNum, boolean add) {
456 boolean success = true;
457 String errorPath = KFSConstants.EMPTY_STRING;
458 // first check to see if the level code is filled in
459 if (checkEmptyValue(objectCodeGlobal.getFinancialObjectLevelCode())) {
460 ObjectLevel objLevel = objectLevelService.getByPrimaryId(dtl.getChartOfAccountsCode(), objectCodeGlobal.getFinancialObjectLevelCode());
461 if (ObjectUtils.isNull(objLevel)) {
462 success &= false;
463 String[] errorParameters = { objectCodeGlobal.getFinancialObjectLevelCode(), dtl.getChartOfAccountsCode() };
464 if (add) {
465 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "chartOfAccountsCode";
466 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_OBJ_LEVEL, errorParameters);
467 }
468 else {
469 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
470 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_OBJ_LEVEL, errorParameters);
471 }
472 }
473 return success;
474
475 }
476 else {
477 GlobalVariables.getMessageMap().putError("financialObjectLevelCode", KFSKeyConstants.ERROR_REQUIRED, "Object Level Code");
478 success &= false;
479 }
480 return success;
481 }
482
483 /**
484 * This method checks the simple rules for all lines at once and gets called on save, submit, etc. but not on add
485 *
486 * <ul>
487 * <li>{@link ObjectCodeGlobalRule#checkFiscalYearAllLines(ObjectCodeGlobal)} </li>
488 * <li>{@link ObjectCodeGlobalRule#checkChartAllLines(ObjectCodeGlobal)} </li>
489 * <li>{@link ObjectCodeGlobalRule#checkObjectLevelCodeAllLines(ObjectCodeGlobal)} </li>
490 * <li>{@link ObjectCodeGlobalRule#checkNextYearObjectCodeAllLines(ObjectCodeGlobal)} </li>
491 * <li>{@link ObjectCodeGlobalRule#checkReportsToObjectCodeAllLines(ObjectCodeGlobal)} </li>
492 * </ul>
493 * @return
494 */
495 protected boolean checkSimpleRulesAllLines() {
496 boolean success = true;
497 // check if there are any object codes and accounts, if either fails this should fail
498 if (!checkForObjectCodeGlobalDetails(objectCodeGlobal.getObjectCodeGlobalDetails())) {
499 success = false;
500 }
501 else {
502 // check object codes
503 success &= checkFiscalYearAllLines(objectCodeGlobal);
504
505 // check chart code
506 success &= checkChartAllLines(objectCodeGlobal);
507
508 // check object level code
509 success &= checkObjectLevelCodeAllLines(objectCodeGlobal);
510
511 // check next year object code
512 success &= checkNextYearObjectCodeAllLines(objectCodeGlobal);
513
514 // check reports to object code
515 success &= checkReportsToObjectCodeAllLines(objectCodeGlobal);
516
517 }
518 return success;
519 }
520
521 /**
522 *
523 * This checks to make sure that there is at least one {@link ObjectCodeGlobalDetail} in the collection
524 * @param objectCodeGlobalDetails
525 * @return false if the collection is empty or null
526 */
527 protected boolean checkForObjectCodeGlobalDetails(List<ObjectCodeGlobalDetail> objectCodeGlobalDetails) {
528 if (objectCodeGlobalDetails == null || objectCodeGlobalDetails.size() == 0) {
529 putFieldError(KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_NO_CHART_FISCAL_YEAR);
530 return false;
531 }
532 return true;
533 }
534
535 /**
536 *
537 * This method calls {@link ObjectCodeGlobalRule#checkFiscalYear(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object
538 * @param objectCodeGlobal
539 * @return true if all lines pass
540 */
541 protected boolean checkFiscalYearAllLines(ObjectCodeGlobal objectCodeGlobal) {
542 boolean success = true;
543 int i = 0;
544 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
545
546 // check fiscal year first
547 success &= checkFiscalYear(objectCodeGlobal, objectCodeGlobalDetail, i, false);
548
549 // increment counter for sub object changes list
550 i++;
551 }
552
553 return success;
554 }
555
556 /**
557 *
558 * This method calls {@link ObjectCodeGlobalRule#checkChartOnObjCodeDetails(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object
559 *
560 * @param ocChangeDocument
561 * @return true if all lines pass
562 */
563 protected boolean checkChartAllLines(ObjectCodeGlobal ocChangeDocument) {
564 boolean success = true;
565 int i = 0;
566 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : ocChangeDocument.getObjectCodeGlobalDetails()) {
567
568 // check chart
569 success &= checkChartOnObjCodeDetails(ocChangeDocument, objectCodeGlobalDetail, i, false);
570 // increment counter for sub object changes list
571 i++;
572 }
573
574 return success;
575 }
576
577
578 /**
579 *
580 * This method calls {@link ObjectCodeGlobalRule#checkReportsToObjectCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object
581 *
582 * @param objectCodeGlobalDocument2
583 * @return true if all lines pass
584 */
585 protected boolean checkReportsToObjectCodeAllLines(ObjectCodeGlobal objectCodeGlobalDocument2) {
586 boolean success = true;
587 int i = 0;
588 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
589
590 // check fiscal year first
591 success &= checkReportsToObjectCode(objectCodeGlobal, objectCodeGlobalDetail, i, false);
592
593 // increment counter for sub object changes list
594 i++;
595 }
596
597 return success;
598 }
599
600 /**
601 *
602 * This method calls {@link ObjectCodeGlobalRule#checkNextYearObjectCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object
603 *
604 * @param objectCodeGlobalDocument2
605 * @return true if all lines pass
606 */
607 protected boolean checkNextYearObjectCodeAllLines(ObjectCodeGlobal objectCodeGlobalDocument2) {
608 boolean success = true;
609 int i = 0;
610 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
611
612 // check fiscal year first
613 success &= checkNextYearObjectCode(objectCodeGlobal, objectCodeGlobalDetail, i, false);
614
615 // increment counter for sub object changes list
616 i++;
617 }
618
619 return success;
620 }
621
622 /**
623 *
624 * This method calls {@link ObjectCodeGlobalRule#checkObjectLevelCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object
625 *
626 * @param objectCodeGlobalDocument2
627 * @return true if all lines pass
628 */
629 protected boolean checkObjectLevelCodeAllLines(ObjectCodeGlobal objectCodeGlobalDocument2) {
630 boolean success = true;
631 int i = 0;
632 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
633
634 // check fiscal year first
635 success &= checkObjectLevelCode(objectCodeGlobal, objectCodeGlobalDetail, i, false);
636
637 // increment counter for sub object changes list
638 i++;
639 }
640
641 return success;
642 }
643
644 /**
645 *
646 * This checks to make sure that the fiscal year has been entered
647 * @param objectCodeGlobal
648 * @param objectCodeGlobalDetail
649 * @param lineNum
650 * @param add
651 * @return false if no fiscal year value
652 */
653 protected boolean checkFiscalYear(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail objectCodeGlobalDetail, int lineNum, boolean add) {
654 boolean success = true;
655 String errorPath = KFSConstants.EMPTY_STRING;
656 // first must have an actual fiscal year
657 if (objectCodeGlobalDetail.getUniversityFiscalYear() == null) {
658 if (add) {
659 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "universityFiscalYear";
660 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_FISCAL_YEAR_MUST_EXIST);
661 }
662 else {
663 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "universityFiscalYear";
664 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_FISCAL_YEAR_MUST_EXIST);
665 }
666 success &= false;
667 return success;
668 }
669
670 return success;
671 }
672
673 /**
674 *
675 * This checks to make sure that the chart of accounts for the detail object has been filled in
676 * @param objectCodeGlobal
677 * @param objectCodeGlobalDetail
678 * @param lineNum
679 * @param add
680 * @return false if chart of accounts code null
681 */
682 protected boolean checkChartOnObjCodeDetails(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail objectCodeGlobalDetail, int lineNum, boolean add) {
683 boolean success = true;
684 String errorPath = KFSConstants.EMPTY_STRING;
685 // first must have an actual fiscal year
686 if (objectCodeGlobalDetail.getChartOfAccounts() == null) {
687 if (add) {
688 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "chartOfAccountsCode";
689 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_CHART_MUST_EXIST);
690 }
691 else {
692 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
693 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_CHART_MUST_EXIST);
694 }
695 success &= false;
696 return success;
697 }
698
699 return success;
700 }
701
702 protected void setObjectCodeService(ObjectCodeService objectCodeService) {
703 this.objectCodeService = objectCodeService;
704
705 }
706
707
708 protected void setObjectLevelService(ObjectLevelService objectLevelService) {
709 this.objectLevelService = objectLevelService;
710
711 }
712 }