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.external.kc.document.validation.impl;
017
018 import java.util.Calendar;
019 import java.util.Collection;
020 import java.util.Date;
021 import java.util.HashMap;
022 import java.util.Map;
023
024 import org.apache.commons.lang.StringUtils;
025 import org.apache.commons.lang.time.DateUtils;
026 import org.kuali.kfs.coa.businessobject.Account;
027 import org.kuali.kfs.coa.businessobject.FundGroup;
028 import org.kuali.kfs.coa.businessobject.IndirectCostRecoveryRateDetail;
029 import org.kuali.kfs.coa.businessobject.SubFundGroup;
030 import org.kuali.kfs.coa.service.AccountService;
031 import org.kuali.kfs.integration.cg.ContractsAndGrantsConstants;
032 import org.kuali.kfs.integration.cg.ContractsAndGrantsModuleService;
033 import org.kuali.kfs.integration.cg.ContractsAndGrantsUnit;
034 import org.kuali.kfs.integration.cg.dto.AccountCreationStatusDTO;
035 import org.kuali.kfs.integration.cg.dto.AccountParametersDTO;
036 import org.kuali.kfs.module.external.kc.KcConstants;
037 import org.kuali.kfs.module.external.kc.businessobject.AccountAutoCreateDefaults;
038 import org.kuali.kfs.module.external.kc.service.impl.AccountCreationServiceImpl;
039 import org.kuali.kfs.sys.KFSConstants;
040 import org.kuali.kfs.sys.KFSKeyConstants;
041 import org.kuali.kfs.sys.KFSPropertyConstants;
042 import org.kuali.kfs.sys.context.SpringContext;
043 import org.kuali.kfs.sys.document.validation.impl.KfsMaintenanceDocumentRuleBase;
044 import org.kuali.kfs.sys.service.UniversityDateService;
045 import org.kuali.rice.kim.bo.Person;
046 import org.kuali.rice.kns.document.MaintenanceDocument;
047 import org.kuali.rice.kns.service.DataDictionaryService;
048 import org.kuali.rice.kns.service.DictionaryValidationService;
049 import org.kuali.rice.kns.service.KualiModuleService;
050 import org.kuali.rice.kns.service.ParameterService;
051 import org.kuali.rice.kns.util.GlobalVariables;
052 import org.kuali.rice.kns.util.KNSConstants;
053 import org.kuali.rice.kns.util.ObjectUtils;
054
055 /**
056 * Business rule(s) applicable to AccountMaintenance documents.
057 */
058 public class AccountAutoCreateDefaultsRule extends org.kuali.kfs.coa.document.validation.impl.AccountRule {
059
060 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountAutoCreateDefaultsRule.class);
061 protected static ParameterService parameterService;
062
063 protected AccountService accountService;
064 protected ContractsAndGrantsModuleService contractsAndGrantsModuleService;
065
066 protected AccountAutoCreateDefaults oldAccountAutoCreateDefaults;
067 protected AccountAutoCreateDefaults newAccountAutoCreateDefaults;
068
069 public AccountAutoCreateDefaultsRule() {
070
071 // Pseudo-inject some services.
072 //
073 // This approach is being used to make it simpler to convert the Rule classes
074 // to spring-managed with these services injected by Spring at some later date.
075 // When this happens, just remove these calls to the setters with
076 // SpringContext, and configure the bean defs for spring.
077 this.setContractsAndGrantsModuleService(SpringContext.getBean(ContractsAndGrantsModuleService.class));
078 accountService = SpringContext.getBean(AccountService.class);
079 }
080
081 /**
082 * This method sets the convenience objects like newAccountAutoCreateDefaults and oldAccountAutoCreateDefaults, so you have
083 * short and easy handles to the new and old objects contained in the maintenance document. It also calls the
084 * BusinessObjectBase.refresh(), which will attempt to load all sub-objects from the DB by their primary keys, if available.
085 */
086 public void setupConvenienceObjects() {
087
088 // setup oldAccountAutoCreateDefaults convenience objects, make sure all possible sub-objects are populated
089 oldAccountAutoCreateDefaults = (AccountAutoCreateDefaults) super.getOldBo();
090
091 // setup newAccountAutoCreateDefaults convenience objects, make sure all possible sub-objects are populated
092 newAccountAutoCreateDefaults = (AccountAutoCreateDefaults) super.getNewBo();
093 }
094
095 /**
096 * This method calls the route rules but does not fail if any of them fail (this only happens on routing)
097 *
098 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
099 */
100 protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
101
102 LOG.info("processCustomSaveDocumentBusinessRules called");
103 // call the route rules to report all of the messages, but ignore the result
104 processCustomRouteDocumentBusinessRules(document);
105
106 // Save always succeeds, even if there are business rule failures
107 return true;
108 }
109
110 /**
111 * This method calls the following rules: checkAccountGuidelinesValidation checkEmptyValues checkGeneralRules checkCloseAccount
112 * checkContractsAndGrants checkExpirationDate checkFundGroup checkSubFundGroup checkFiscalOfficerIsValidKualiUser this rule
113 * will fail on routing
114 *
115 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
116 */
117 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
118 // default to success
119 boolean success = true;
120
121 LOG.info("processCustomRouteDocumentBusinessRules called");
122 setupConvenienceObjects();
123
124 success &= checkEmptyValues(document);
125 success &= checkGeneralRules(document);
126 success &= checkContractsAndGrants(document);
127 success &= checkIncomeStreamAccountRule();
128 success &= checkRequiredKcUnit(newAccountAutoCreateDefaults, document.isNew());
129
130 return success;
131 }
132
133 protected boolean checkEmptyValues(MaintenanceDocument maintenanceDocument) {
134
135 LOG.info("checkEmptyValues called");
136
137 boolean success = true;
138
139 // this set confirms that all fields which are grouped (ie, foreign keys of a reference
140 // object), must either be none filled out, or all filled out.
141 success &= checkForPartiallyFilledOutReferenceForeignKeys("continuationAccount");
142 success &= checkForPartiallyFilledOutReferenceForeignKeys("incomeStreamAccount");
143
144 success &= checkForPartiallyFilledOutReferenceForeignKeys("reportsToAccount");
145 success &= checkForPartiallyFilledOutReferenceForeignKeys("indirectCostRecoveryAcct");
146
147 return success;
148 }
149
150 protected boolean checkGeneralRules(MaintenanceDocument maintenanceDocument) {
151
152 LOG.info("checkGeneralRules called");
153 Person fiscalOfficer = newAccountAutoCreateDefaults.getAccountFiscalOfficerUser();
154 Person accountManager = newAccountAutoCreateDefaults.getAccountManagerUser();
155 Person accountSupervisor = newAccountAutoCreateDefaults.getAccountSupervisoryUser();
156
157 boolean success = true;
158
159 // check FringeBenefit account rules
160 success &= checkFringeBenefitAccountRule();
161
162 if (ObjectUtils.isNotNull(fiscalOfficer) && !getDocumentHelperService().getDocumentAuthorizer(maintenanceDocument).isAuthorized(maintenanceDocument, KFSConstants.ParameterNamespaces.CHART, KFSConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER, fiscalOfficer.getPrincipalId())) {
163 super.putFieldError("accountFiscalOfficerUser.principalName", KFSKeyConstants.ERROR_USER_MISSING_PERMISSION, new String[] {fiscalOfficer.getName(), KFSConstants.ParameterNamespaces.CHART, KFSConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER});
164 success = false;
165 }
166 if (ObjectUtils.isNotNull(accountSupervisor) && !getDocumentHelperService().getDocumentAuthorizer(maintenanceDocument).isAuthorized(maintenanceDocument, KFSConstants.ParameterNamespaces.CHART, KFSConstants.PermissionNames.SERVE_AS_ACCOUNT_SUPERVISOR, accountSupervisor.getPrincipalId())) {
167 super.putFieldError("accountSupervisoryUser.principalName", KFSKeyConstants.ERROR_USER_MISSING_PERMISSION, new String[] {accountSupervisor.getName(), KFSConstants.ParameterNamespaces.CHART, KFSConstants.PermissionNames.SERVE_AS_ACCOUNT_SUPERVISOR});
168 success = false;
169 }
170 if (ObjectUtils.isNotNull(accountManager) && !getDocumentHelperService().getDocumentAuthorizer(maintenanceDocument).isAuthorized(maintenanceDocument, KFSConstants.ParameterNamespaces.CHART, KFSConstants.PermissionNames.SERVE_AS_ACCOUNT_MANAGER, accountManager.getPrincipalId())) {
171 super.putFieldError("accountManagerUser.principalName", KFSKeyConstants.ERROR_USER_MISSING_PERMISSION, new String[] {accountManager.getName(), KFSConstants.ParameterNamespaces.CHART, KFSConstants.PermissionNames.SERVE_AS_ACCOUNT_MANAGER});
172 success = false;
173 }
174
175 // the supervisor cannot be the same as the fiscal officer or account manager.
176 if (isSupervisorSameAsFiscalOfficer()) {
177 success &= false;
178 putFieldError("accountsSupervisorySystemsIdentifier", KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ACCT_SUPER_CANNOT_BE_FISCAL_OFFICER);
179 }
180 if (isSupervisorSameAsManager()) {
181 success &= false;
182 putFieldError("accountManagerSystemIdentifier", KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ACCT_SUPER_CANNOT_BE_ACCT_MGR);
183 }
184
185 // disallow continuation account being expired
186 if (isContinuationAccountExpired()) {
187 success &= false;
188 putFieldError("continuationAccountNumber", KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ACCOUNT_EXPIRED_CONTINUATION);
189 }
190
191 return success;
192 }
193
194 protected boolean checkFringeBenefitAccountRule() {
195
196 boolean result = true;
197
198 // if this account is selected as a Fringe Benefit Account, then we have nothing
199 // to test, so exit
200 if (newAccountAutoCreateDefaults.isAccountsFringesBnftIndicator()) {
201 return true;
202 }
203
204 // if fringe benefit is not selected ... continue processing
205
206 // fringe benefit account number is required
207 if (StringUtils.isBlank(newAccountAutoCreateDefaults.getReportsToAccountNumber())) {
208 putFieldError("reportsToAccountNumber", KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_RPTS_TO_ACCT_REQUIRED_IF_FRINGEBENEFIT_FALSE);
209 result &= false;
210 }
211
212 // fringe benefit chart of accounts code is required
213 if (StringUtils.isBlank(newAccountAutoCreateDefaults.getReportsToChartOfAccountsCode())) {
214 putFieldError("reportsToChartOfAccountsCode", KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_RPTS_TO_ACCT_REQUIRED_IF_FRINGEBENEFIT_FALSE);
215 result &= false;
216 }
217
218 // if either of the fringe benefit account fields are not present, then we're done
219 if (result == false) {
220 return result;
221 }
222
223 // attempt to load the fringe benefit account
224 Account fringeBenefitAccount = accountService.getByPrimaryId(newAccountAutoCreateDefaults.getReportsToChartOfAccountsCode(), newAccountAutoCreateDefaults.getReportsToAccountNumber());
225
226 // fringe benefit account must exist
227 if (fringeBenefitAccount == null) {
228 putFieldError("reportsToAccountNumber", KFSKeyConstants.ERROR_EXISTENCE, getFieldLabel(Account.class, "reportsToAccountNumber"));
229 return false;
230 }
231
232 // fringe benefit account must be active
233 if (!fringeBenefitAccount.isActive()) {
234 putFieldError("reportsToAccountNumber", KFSKeyConstants.ERROR_INACTIVE, getFieldLabel(Account.class, "reportsToAccountNumber"));
235 result &= false;
236 }
237
238 // make sure the fringe benefit account specified is set to fringe benefits = Y
239 if (!fringeBenefitAccount.isAccountsFringesBnftIndicator()) {
240 putFieldError("reportsToAccountNumber", KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_RPTS_TO_ACCT_MUST_BE_FLAGGED_FRINGEBENEFIT, fringeBenefitAccount.getChartOfAccountsCode() + "-" + fringeBenefitAccount.getAccountNumber());
241 result &= false;
242 }
243
244 return result;
245 }
246
247 protected boolean isSupervisorSameAsFiscalOfficer() {
248 return areTwoUsersTheSame(newAccountAutoCreateDefaults.getAccountSupervisoryUser(), newAccountAutoCreateDefaults.getAccountFiscalOfficerUser());
249 }
250
251 protected boolean isSupervisorSameAsManager() {
252 return areTwoUsersTheSame(newAccountAutoCreateDefaults.getAccountSupervisoryUser(), newAccountAutoCreateDefaults.getAccountManagerUser());
253 }
254
255 protected boolean isContinuationAccountExpired() {
256
257 boolean result = false;
258
259 String chartCode = newAccountAutoCreateDefaults.getContinuationFinChrtOfAcctCd();
260 String accountNumber = newAccountAutoCreateDefaults.getContinuationAccountNumber();
261
262 // if either chartCode or accountNumber is not entered, then we
263 // can't continue, so exit
264 if (StringUtils.isBlank(chartCode) || StringUtils.isBlank(accountNumber)) {
265 return result;
266 }
267
268 // attempt to retrieve the continuation account from the DB
269 Account continuation = accountService.getByPrimaryId(chartCode, accountNumber);
270
271 // if the object doesn't exist, then we can't continue, so exit
272 if (ObjectUtils.isNull(continuation)) {
273 return result;
274 }
275
276 // at this point, we have a valid continuation account, so we just need to
277 // know whether its expired or not
278 result = continuation.isExpired();
279
280 return result;
281 }
282
283 protected boolean checkContractsAndGrants(MaintenanceDocument maintenanceDocument) {
284
285 LOG.info("checkContractsAndGrants called");
286
287 boolean success = true;
288
289 // Certain C&G fields are required if the Account belongs to the CG Fund Group
290 success &= checkCgRequiredFields();
291
292 // Income Stream account is required if this account is CG fund group,
293 // or GF (general fund) fund group (with some exceptions)
294 success &= checkIncomeStreamValid();
295
296 // check if the new account has a valid responsibility id
297 if (!ObjectUtils.isNull(newAccountAutoCreateDefaults)) {
298 Account account = new Account();
299 account.setContractsAndGrantsAccountResponsibilityId(newAccountAutoCreateDefaults.getContractsAndGrantsAccountResponsibilityId());
300 final boolean hasValidAccountResponsibility = contractsAndGrantsModuleService.hasValidAccountReponsiblityIdIfNotNull(account);
301 if (!hasValidAccountResponsibility) {
302 success &= hasValidAccountResponsibility;
303 putFieldError("contractsAndGrantsAccountResponsibilityId", KFSKeyConstants.ERROR_DOCUMENT_ACCTMAINT_INVALID_CG_RESPONSIBILITY , new String[] { newAccountAutoCreateDefaults.getContractsAndGrantsAccountResponsibilityId().toString(), newAccountAutoCreateDefaults.getChartOfAccountsCode(), "" });
304 }
305 }
306
307 return success;
308 }
309
310 protected boolean checkCgRequiredFields() {
311
312 boolean result = true;
313
314 // Certain C&G fields are required if the Account belongs to the CG Fund Group
315 if (ObjectUtils.isNotNull(newAccountAutoCreateDefaults.getSubFundGroup())) {
316 if (getSubFundGroupService().isForContractsAndGrants(newAccountAutoCreateDefaults.getSubFundGroup())) {
317
318 result &= checkEmptyBOField("indirectCostRcvyFinCoaCode", newAccountAutoCreateDefaults.getIndirectCostRcvyFinCoaCode(), replaceTokens(KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ICR_CHART_CODE_CANNOT_BE_EMPTY));
319 result &= checkEmptyBOField("indirectCostRecoveryAcctNbr", newAccountAutoCreateDefaults.getIndirectCostRecoveryAcctNbr(), replaceTokens(KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ICR_ACCOUNT_CANNOT_BE_EMPTY));
320 }
321 else {
322 // this is not a C&G fund group. So users should not fill in any fields in the C&G tab.
323 result &= checkCGFieldNotFilledIn("indirectCostRcvyFinCoaCode");
324 result &= checkCGFieldNotFilledIn("indirectCostRecoveryAcctNbr");
325 }
326 }
327 return result;
328 }
329
330 protected boolean checkIncomeStreamValid() {
331 // if the subFundGroup object is null, we can't test, so exit
332 if (ObjectUtils.isNull(newAccountAutoCreateDefaults.getSubFundGroup())) {
333 return true;
334 }
335 String subFundGroupCode = newAccountAutoCreateDefaults.getSubFundGroupCode().trim();
336 String fundGroupCode = newAccountAutoCreateDefaults.getSubFundGroup().getFundGroupCode().trim();
337 boolean valid = true;
338 if (getParameterService().getParameterEvaluator(Account.class, KFSConstants.ChartApcParms.INCOME_STREAM_ACCOUNT_REQUIRING_FUND_GROUPS, fundGroupCode).evaluationSucceeds()) {
339 if (getParameterService().getParameterEvaluator(Account.class, KFSConstants.ChartApcParms.INCOME_STREAM_ACCOUNT_REQUIRING_SUB_FUND_GROUPS, subFundGroupCode).evaluationSucceeds()) {
340 if (StringUtils.isBlank(newAccountAutoCreateDefaults.getIncomeStreamFinancialCoaCode())) {
341 putFieldError(KFSPropertyConstants.INCOME_STREAM_CHART_OF_ACCOUNTS_CODE, KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_INCOME_STREAM_ACCT_COA_CANNOT_BE_EMPTY, new String[] { getDdService().getAttributeLabel(FundGroup.class, KFSConstants.FUND_GROUP_CODE_PROPERTY_NAME), fundGroupCode, getDdService().getAttributeLabel(SubFundGroup.class, KFSConstants.SUB_FUND_GROUP_CODE_PROPERTY_NAME), subFundGroupCode });
342 valid = false;
343 }
344 if (StringUtils.isBlank(newAccountAutoCreateDefaults.getIncomeStreamAccountNumber())) {
345 putFieldError(KFSPropertyConstants.INCOME_STREAM_ACCOUNT_NUMBER, KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_INCOME_STREAM_ACCT_NBR_CANNOT_BE_EMPTY, new String[] { getDdService().getAttributeLabel(FundGroup.class, KFSConstants.FUND_GROUP_CODE_PROPERTY_NAME), fundGroupCode, getDdService().getAttributeLabel(SubFundGroup.class, KFSConstants.SUB_FUND_GROUP_CODE_PROPERTY_NAME), subFundGroupCode});
346 valid = false;
347 }
348 }
349 }
350 return valid;
351 }
352
353 protected boolean checkCGFieldNotFilledIn(String propertyName) {
354 boolean success = true;
355 Object value = ObjectUtils.getPropertyValue(newAccountAutoCreateDefaults, propertyName);
356 if ((value instanceof String && !StringUtils.isBlank(value.toString())) || (value != null)) {
357 success = false;
358 putFieldError(propertyName, KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_CG_FIELDS_FILLED_FOR_NON_CG_ACCOUNT, new String[] { newAccountAutoCreateDefaults.getSubFundGroupCode() });
359 }
360
361 return success;
362 }
363
364 protected boolean checkIncomeStreamAccountRule() {
365 // KFSMI-4877: if fund group is in system parameter values then income stream account number must exist.
366 if ( ObjectUtils.isNotNull(newAccountAutoCreateDefaults.getSubFundGroup()) && StringUtils.isNotBlank(newAccountAutoCreateDefaults.getSubFundGroup().getFundGroupCode())) {
367 if (ObjectUtils.isNull(newAccountAutoCreateDefaults.getIncomeStreamAccount())) {
368 String incomeStreamRequiringFundGroupCode = SpringContext.getBean(ParameterService.class).getParameterValue(Account.class, KFSConstants.ChartApcParms.INCOME_STREAM_ACCOUNT_REQUIRING_FUND_GROUPS);
369 if (StringUtils.containsIgnoreCase(newAccountAutoCreateDefaults.getSubFundGroup().getFundGroupCode(), incomeStreamRequiringFundGroupCode)) {
370 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ACCOUNT_NUMBER, KFSKeyConstants.ERROR_DOCUMENT_BA_NO_INCOME_STREAM_ACCOUNT, "");
371 return false;
372 }
373 }
374 }
375 return true;
376 }
377
378 /**
379 * This method checks to make sure that the kcUnit field exists and is entered correctly
380 *
381 * @param newAccountAutoCreateDefaults
382 * @return true/false
383 */
384 protected boolean checkRequiredKcUnit(AccountAutoCreateDefaults newAccountAutoCreateDefaults, boolean isNew) {
385
386 boolean result = true;
387 try {
388 ContractsAndGrantsUnit unitDTO = newAccountAutoCreateDefaults.getUnitDTO();
389 unitDTO = (ContractsAndGrantsUnit) SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(ContractsAndGrantsUnit.class).retrieveExternalizableBusinessObjectIfNecessary(newAccountAutoCreateDefaults, unitDTO, "unitDTO");
390 if (unitDTO == null) {
391 putFieldError(KcConstants.AccountCreationDefaults.KcUnit, ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_ACCOUNT_PARAMS_UNIT_NOTFOUND, newAccountAutoCreateDefaults.getKcUnit());
392 result &= false;
393 }
394 // in the case of new accounts check if KcUnit exists already in accountAutoCreateDefaults table - if so reject
395 if (isNew) {
396 // check for new copy and new conditions
397 String kcUnit = newAccountAutoCreateDefaults.getKcUnit();
398 if ((kcUnit != null)) {
399 HashMap<String, String> map = new HashMap<String,String>();
400
401 map.put(KcConstants.AccountCreationDefaults.KcUnit, kcUnit);
402 Collection <AccountAutoCreateDefaults> accountAutoCreateDefaultList = boService.findMatching(AccountAutoCreateDefaults.class, map);
403 if (accountAutoCreateDefaultList == null || (! accountAutoCreateDefaultList.isEmpty())) {
404 putFieldError(KcConstants.AccountCreationDefaults.KcUnit, ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_ACCOUNT_ALREADY_DEFINED, newAccountAutoCreateDefaults.getKcUnit());
405 result &= false;
406 }
407 }
408 }
409 return result;
410 }
411 catch (Exception ex) {
412 putFieldError(KcConstants.AccountCreationDefaults.KcUnit, ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_ACCOUNT_PARAMS_UNIT_NOTFOUND, newAccountAutoCreateDefaults.getKcUnit());
413 return false;
414 }
415 }
416
417
418 /**
419 * Sets the contractsAndGrantsModuleService attribute value.
420 *
421 * @param contractsAndGrantsModuleService The contractsAndGrantsModuleService to set.
422 */
423 public void setContractsAndGrantsModuleService(ContractsAndGrantsModuleService contractsAndGrantsModuleService) {
424 this.contractsAndGrantsModuleService = contractsAndGrantsModuleService;
425 }
426
427
428 public ParameterService getParameterService() {
429 if (parameterService == null) {
430 parameterService = SpringContext.getBean(ParameterService.class);
431 }
432 return parameterService;
433 }
434
435 }