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.bc.document.validation.impl; 017 018 import org.kuali.kfs.coa.businessobject.Organization; 019 import org.kuali.kfs.coa.service.ChartService; 020 import org.kuali.kfs.coa.service.OrganizationService; 021 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionOrganizationReports; 022 import org.kuali.kfs.module.bc.document.service.BudgetConstructionOrganizationReportsService; 023 import org.kuali.kfs.sys.KFSConstants; 024 import org.kuali.kfs.sys.KFSKeyConstants; 025 import org.kuali.kfs.sys.context.SpringContext; 026 import org.kuali.rice.kns.document.MaintenanceDocument; 027 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; 028 import org.kuali.rice.kns.service.ParameterService; 029 import org.kuali.rice.kns.util.GlobalVariables; 030 import org.kuali.rice.kns.util.ObjectUtils; 031 032 public class BudgetConstructionOrganizationReportsRule extends MaintenanceDocumentRuleBase { 033 034 protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BudgetConstructionOrganizationReportsRule.class); 035 036 037 protected OrganizationService orgService; 038 protected ChartService chartService; 039 protected BudgetConstructionOrganizationReportsService bcOrgReportsService; 040 041 protected BudgetConstructionOrganizationReports oldBCOrgReports; 042 protected BudgetConstructionOrganizationReports newBCOrgReports; 043 044 045 public BudgetConstructionOrganizationReportsRule() { 046 super(); 047 048 049 // Pseudo-inject some services. 050 // 051 // This approach is being used to make it simpler to convert the Rule classes 052 // to spring-managed with these services injected by Spring at some later date. 053 // When this happens, just remove these calls to the setters with 054 // SpringContext, and configure the bean defs for spring. 055 this.setOrgService(SpringContext.getBean(OrganizationService.class)); 056 this.setChartService(SpringContext.getBean(ChartService.class)); 057 this.setBCOrgReportsService(SpringContext.getBean(BudgetConstructionOrganizationReportsService.class)); 058 } 059 060 /** 061 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 062 */ 063 protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) { 064 065 boolean success = true; 066 067 LOG.info("Entering processCustomApproveDocumentBusinessRules()"); 068 069 // check reporting hierarchy is valid 070 success &= checkSimpleRules(document); 071 072 return success; 073 } 074 075 /** 076 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 077 */ 078 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) { 079 080 boolean success = true; 081 082 LOG.info("Entering processCustomRouteDocumentBusinessRules()"); 083 084 // check reporting hierarchy is valid 085 success &= checkSimpleRules(document); 086 087 return success; 088 } 089 090 /** 091 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 092 */ 093 protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) { 094 095 LOG.info("Entering processCustomSaveDocumentBusinessRules()"); 096 097 // check reporting hierarchy is valid 098 checkSimpleRules(document); 099 100 return true; 101 } 102 103 104 protected boolean checkSimpleRules(MaintenanceDocument document) { 105 106 boolean success = true; 107 String lastReportsToChartOfAccountsCode; 108 String lastReportsToOrganizationCode; 109 boolean continueSearch; 110 BudgetConstructionOrganizationReports tempBCOrgReports; 111 Organization tempOrg; 112 Integer loopCount; 113 Integer maxLoopCount = 40; 114 115 boolean orgMustReportToSelf = false; 116 tempOrg = null; 117 118 // Get the Org business object so that we can check the org type 119 if (ObjectUtils.isNotNull(newBCOrgReports.getChartOfAccountsCode()) && ObjectUtils.isNotNull(newBCOrgReports.getOrganizationCode())) { 120 tempOrg = orgService.getByPrimaryId(newBCOrgReports.getChartOfAccountsCode(), newBCOrgReports.getOrganizationCode()); 121 122 // Check the Org Type of the Org business object to see if it is the root (reports to self) 123 124 if (ObjectUtils.isNotNull(tempOrg)) { 125 if (SpringContext.getBean(ParameterService.class).getParameterEvaluator(Organization.class, KFSConstants.ChartApcParms.ORG_MUST_REPORT_TO_SELF_ORG_TYPES, tempOrg.getOrganizationTypeCode()).evaluationSucceeds()) { 126 orgMustReportToSelf = true; 127 } 128 } 129 } 130 131 // Reports To Chart/Org should not be same as this Chart/Org 132 // However, allow special case where organization type is listed in the business rules 133 if (ObjectUtils.isNotNull(newBCOrgReports.getReportsToChartOfAccountsCode()) && ObjectUtils.isNotNull(newBCOrgReports.getReportsToOrganizationCode()) && ObjectUtils.isNotNull(newBCOrgReports.getChartOfAccountsCode()) && ObjectUtils.isNotNull(newBCOrgReports.getOrganizationCode())) { 134 if (!orgMustReportToSelf) { 135 136 if ((newBCOrgReports.getReportsToChartOfAccountsCode().equals(newBCOrgReports.getChartOfAccountsCode())) && (newBCOrgReports.getReportsToOrganizationCode().equals(newBCOrgReports.getOrganizationCode()))) { 137 putFieldError("reportsToOrganizationCode", KFSKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_CANNOT_BE_SAME_ORG); 138 success = false; 139 } 140 else { 141 // Don't allow a circular reference on Reports to Chart/Org 142 // terminate the search when a top-level org is found 143 lastReportsToChartOfAccountsCode = newBCOrgReports.getReportsToChartOfAccountsCode(); 144 lastReportsToOrganizationCode = newBCOrgReports.getReportsToOrganizationCode(); 145 continueSearch = true; 146 loopCount = 0; 147 do { 148 tempBCOrgReports = bcOrgReportsService.getByPrimaryId(lastReportsToChartOfAccountsCode, lastReportsToOrganizationCode); 149 loopCount++; 150 ; 151 if (ObjectUtils.isNull(tempBCOrgReports)) { 152 continueSearch = false; 153 // if a null is returned on the first iteration, then the reports-to org does not exist 154 // fail the validation 155 if (loopCount == 1) { 156 putFieldError("reportsToOrganizationCode", KFSKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_MUST_EXIST); 157 success = false; 158 } 159 } 160 else { 161 { 162 // LOG.info("Found Org = " + lastReportsToChartOfAccountsCode + "/" + 163 // lastReportsToOrganizationCode); 164 lastReportsToChartOfAccountsCode = tempBCOrgReports.getReportsToChartOfAccountsCode(); 165 lastReportsToOrganizationCode = tempBCOrgReports.getReportsToOrganizationCode(); 166 167 if ((tempBCOrgReports.getReportsToChartOfAccountsCode().equals(newBCOrgReports.getChartOfAccountsCode())) && (tempBCOrgReports.getReportsToOrganizationCode().equals(newBCOrgReports.getOrganizationCode()))) { 168 putFieldError("reportsToOrganizationCode", KFSKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_CANNOT_BE_CIRCULAR_REF_TO_SAME_ORG); 169 success = false; 170 continueSearch = false; 171 } 172 } 173 } 174 if (loopCount > maxLoopCount) { 175 continueSearch = false; 176 } 177 // stop the search if we reach an org that reports to itself 178 if (continueSearch && (tempBCOrgReports.getReportsToChartOfAccountsCode().equals(tempBCOrgReports.getReportsToChartOfAccountsCode())) && (tempBCOrgReports.getReportsToOrganizationCode().equals(tempBCOrgReports.getOrganizationCode()))) 179 continueSearch = false; 180 181 } while (continueSearch == true); 182 183 } // end else (checking for circular ref) 184 185 } 186 else { // org must report to self (university level organization) 187 if (!(newBCOrgReports.getReportsToChartOfAccountsCode().equals(newBCOrgReports.getChartOfAccountsCode()) && newBCOrgReports.getReportsToOrganizationCode().equals(newBCOrgReports.getOrganizationCode()))) { 188 putFieldError("reportsToOrganizationCode", KFSKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_MUST_BE_SAME_ORG); 189 success = false; 190 } 191 } 192 } 193 194 return success; 195 } 196 197 /** 198 * This method sets the convenience objects like newAccount and oldAccount, so you have short and easy handles to the new and 199 * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load 200 * all sub-objects from the DB by their primary keys, if available. 201 * 202 * @param document - the maintenanceDocument being evaluated 203 */ 204 public void setupConvenienceObjects() { 205 206 // setup oldAccount convenience objects, make sure all possible sub-objects are populated 207 oldBCOrgReports = (BudgetConstructionOrganizationReports) super.getOldBo(); 208 209 // setup newAccount convenience objects, make sure all possible sub-objects are populated 210 newBCOrgReports = (BudgetConstructionOrganizationReports) super.getNewBo(); 211 } 212 213 /** 214 * Sets the orgService attribute value. 215 * 216 * @param orgService The orgService to set. 217 */ 218 public void setOrgService(OrganizationService orgService) { 219 this.orgService = orgService; 220 } 221 222 /** 223 * Sets the chartService attribute value. 224 * 225 * @param chartService The orgService to set. 226 */ 227 public void setChartService(ChartService chartService) { 228 this.chartService = chartService; 229 } 230 231 /** 232 * Sets the bcOrgReportsService attribute value. 233 * 234 * @param bcOrgReportsService The bcOrgReportsService to set. 235 */ 236 public void setBCOrgReportsService(BudgetConstructionOrganizationReportsService bcOrgReportsService) { 237 this.bcOrgReportsService = bcOrgReportsService; 238 } 239 240 } 241