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