001/* 002 * Copyright 2006 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 */ 016package org.kuali.ole.coa.document.validation.impl; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.commons.lang.StringUtils; 025import org.kuali.ole.coa.businessobject.A21IndirectCostRecoveryAccount; 026import org.kuali.ole.coa.businessobject.A21SubAccount; 027import org.kuali.ole.coa.businessobject.IndirectCostRecoveryAccount; 028import org.kuali.ole.coa.businessobject.IndirectCostRecoveryRateDetail; 029import org.kuali.ole.coa.businessobject.SubAccount; 030import org.kuali.ole.coa.service.SubFundGroupService; 031import org.kuali.ole.sys.OLEConstants; 032import org.kuali.ole.sys.OLEKeyConstants; 033import org.kuali.ole.sys.OLEPropertyConstants; 034import org.kuali.ole.sys.context.SpringContext; 035import org.kuali.ole.sys.service.UniversityDateService; 036import org.kuali.rice.kns.document.MaintenanceDocument; 037import org.kuali.rice.kns.service.DataDictionaryService; 038import org.kuali.rice.krad.util.ObjectUtils; 039 040/** 041 * This class implements the business rules specific to the {@link SubAccount} Maintenance Document. 042 */ 043public class SubAccountRule extends IndirectCostRecoveryAccountsRule { 044 045 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SubAccountRule.class); 046 047 protected SubAccount oldSubAccount; 048 protected SubAccount newSubAccount; 049 050 /** 051 * This performs rules checks on document approve 052 * <ul> 053 * <li>{@link SubAccountRule#setCgAuthorized(boolean)}</li> 054 * <li>{@link SubAccountRule#checkForPartiallyEnteredReportingFields()}</li> 055 * <li>{@link SubAccountRule#checkCgRules(MaintenanceDocument)}</li> 056 * </ul> 057 * This rule fails on business rule failures 058 * 059 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 060 */ 061 protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) { 062 LOG.debug("Entering processCustomApproveDocumentBusinessRules()"); 063 064 // check that all sub-objects whose keys are specified have matching objects in the db 065 boolean success = checkForPartiallyEnteredReportingFields(); 066 067 // process CG rules if appropriate 068 success &= checkCgRules(document); 069 070 return success; 071 } 072 073 /** 074 * This performs rules checks on document route 075 * <ul> 076 * <li>{@link SubAccountRule#setCgAuthorized(boolean)}</li> 077 * <li>{@link SubAccountRule#checkForPartiallyEnteredReportingFields()}</li> 078 * <li>{@link SubAccountRule#checkCgRules(MaintenanceDocument)}</li> 079 * </ul> 080 * This rule fails on business rule failures 081 * 082 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 083 */ 084 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) { 085 LOG.debug("Entering processCustomRouteDocumentBusinessRules()"); 086 087 boolean success = true; 088 089 // check that all sub-objects whose keys are specified have matching objects in the db 090 success &= checkForPartiallyEnteredReportingFields(); 091 092 // process CG rules if appropriate 093 success &= checkCgRules(document); 094 095 success &= super.processCustomRouteDocumentBusinessRules(document); 096 return success; 097 } 098 099 /** 100 * This performs rules checks on document save 101 * <ul> 102 * <li>{@link SubAccountRule#setCgAuthorized(boolean)}</li> 103 * <li>{@link SubAccountRule#checkForPartiallyEnteredReportingFields()}</li> 104 * <li>{@link SubAccountRule#checkCgRules(MaintenanceDocument)}</li> 105 * </ul> 106 * This rule does not fail on business rule failures 107 * 108 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 109 */ 110 protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) { 111 112 boolean success = true; 113 114 LOG.debug("Entering processCustomSaveDocumentBusinessRules()"); 115 116 // check that all sub-objects whose keys are specified have matching objects in the db 117 success &= checkForPartiallyEnteredReportingFields(); 118 119 // process CG rules if appropriate 120 success &= checkCgRules(document); 121 122 return success; 123 } 124 125 /** 126 * This method sets the convenience objects like newAccount and oldAccount, so you have short and easy handles to the new and 127 * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load 128 * all sub-objects from the DB by their primary keys, if available. 129 * 130 * @param document - the maintenanceDocument being evaluated 131 */ 132 public void setupConvenienceObjects() { 133 134 // setup oldAccount convenience objects, make sure all possible sub-objects are populated 135 oldSubAccount = (SubAccount) super.getOldBo(); 136 refreshSubObjects(oldSubAccount); 137 138 // setup newAccount convenience objects, make sure all possible sub-objects are populated 139 newSubAccount = (SubAccount) super.getNewBo(); 140 refreshSubObjects(newSubAccount); 141 142 //icr rule checking setup 143 if (newSubAccount.getA21SubAccount() != null){ 144 List<IndirectCostRecoveryAccount> icrAccountList = new ArrayList<IndirectCostRecoveryAccount>( 145 newSubAccount.getA21SubAccount().getA21ActiveIndirectCostRecoveryAccounts()); 146 setActiveIndirectCostRecoveryAccountList(icrAccountList); 147 setBoFieldPath(OLEPropertyConstants.A21INDIRECT_COST_RECOVERY_ACCOUNTS); 148 } 149 } 150 151 /** 152 * Refreshes the references of account 153 * 154 * @param subaccount SubAccount 155 */ 156 void refreshSubObjects(SubAccount subaccount) { 157 if (subaccount != null) { 158 if (subaccount.getA21SubAccount() != null) { 159 subaccount.getA21SubAccount().refreshNonUpdateableReferences(); 160 // refresh contacts 161// if (subaccount.getA21SubAccount().getA21IndirectCostRecoveryAccounts() != null) { 162// for (A21IndirectCostRecoveryAccount icra : subaccount.getA21SubAccount().getA21IndirectCostRecoveryAccounts()) { 163// icra.refreshNonUpdateableReferences(); 164// } 165// } 166 } 167 } 168 } 169 170 /** 171 * This checks that the reporting fields are entered altogether or none at all 172 * 173 * @return false if only one reporting field filled out and not all of them, true otherwise 174 */ 175 protected boolean checkForPartiallyEnteredReportingFields() { 176 177 LOG.debug("Entering checkExistenceAndActive()"); 178 179 boolean success = true; 180 boolean allReportingFieldsEntered = false; 181 boolean anyReportingFieldsEntered = false; 182 183 // set a flag if all three reporting fields are filled (this is separated just for readability) 184 if (StringUtils.isNotEmpty(newSubAccount.getFinancialReportChartCode()) && StringUtils.isNotEmpty(newSubAccount.getFinReportOrganizationCode()) && StringUtils.isNotEmpty(newSubAccount.getFinancialReportingCode())) { 185 allReportingFieldsEntered = true; 186 } 187 188 // set a flag if any of the three reporting fields are filled (this is separated just for readability) 189 if (StringUtils.isNotEmpty(newSubAccount.getFinancialReportChartCode()) || StringUtils.isNotEmpty(newSubAccount.getFinReportOrganizationCode()) || StringUtils.isNotEmpty(newSubAccount.getFinancialReportingCode())) { 190 anyReportingFieldsEntered = true; 191 } 192 193 // if any of the three reporting code fields are filled out, all three must be, or none 194 // if any of the three are entered 195 if (anyReportingFieldsEntered && !allReportingFieldsEntered) { 196 putGlobalError(OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_RPTCODE_ALL_FIELDS_IF_ANY_FIELDS); 197 success &= false; 198 } 199 200 return success; 201 } 202 203 /** 204 * This checks to make sure that if cgAuthorized is false it succeeds immediately, otherwise it checks that all the information 205 * for CG is correctly entered and identified including: 206 * <ul> 207 * <li>If the {@link SubFundGroup} isn't for Contracts and Grants then check to make sure that the cost share and ICR fields are 208 * not empty</li> 209 * <li>If it isn't a child of CG, then the SubAccount must be of type ICR</li> 210 * </ul> 211 * 212 * @param document 213 * @return true if the user is not authorized to change CG fields, otherwise it checks the above conditions 214 */ 215 protected boolean checkCgRules(MaintenanceDocument document) { 216 217 boolean success = true; 218 219 // short circuit if the parent account is NOT part of a CG fund group 220 boolean a21SubAccountRefreshed = false; 221 if (ObjectUtils.isNotNull(newSubAccount.getAccount())) { 222 if (ObjectUtils.isNotNull(newSubAccount.getAccount().getSubFundGroup())) { 223 224 // compare them, exit if the account isn't for contracts and grants 225 if (!SpringContext.getBean(SubFundGroupService.class).isForContractsAndGrants(newSubAccount.getAccount().getSubFundGroup())) { 226 227 // KULCOA-1116 - Check if CG CS and CG ICR are empty, if not throw an error 228 if (checkCgCostSharingIsEmpty() == false) { 229 putFieldError("a21SubAccount.costShareChartOfAccountCode", OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_NON_FUNDED_ACCT_CS_INVALID, new String[] { SpringContext.getBean(SubFundGroupService.class).getContractsAndGrantsDenotingAttributeLabel(), SpringContext.getBean(SubFundGroupService.class).getContractsAndGrantsDenotingValueForMessage() }); 230 success = false; 231 } 232 233 if (checkCgIcrIsEmpty() == false) { 234 putFieldError("a21SubAccount.indirectCostRecoveryTypeCode", OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_NON_FUNDED_ACCT_ICR_INVALID, new String[] { SpringContext.getBean(SubFundGroupService.class).getContractsAndGrantsDenotingAttributeLabel(), SpringContext.getBean(SubFundGroupService.class).getContractsAndGrantsDenotingValueForMessage() }); 235 success = false; 236 } 237 238 // KULRNE-4660 - this isn't the child of a CG account; sub account must be ICR type 239 if (!ObjectUtils.isNull(newSubAccount.getA21SubAccount())) { 240 // KFSMI-798 - refresh() changed to refreshNonUpdateableReferences() 241 // All references for A21SubAccount are non-updatable 242 newSubAccount.getA21SubAccount().refreshNonUpdateableReferences(); 243 a21SubAccountRefreshed = true; 244 if (StringUtils.isEmpty(newSubAccount.getA21SubAccount().getSubAccountTypeCode()) || !newSubAccount.getA21SubAccount().getSubAccountTypeCode().equals(OLEConstants.SubAccountType.EXPENSE)) { 245 putFieldError("a21SubAccount.subAccountTypeCode", OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_NON_FUNDED_ACCT_SUB_ACCT_TYPE_CODE_INVALID, new String[] { SpringContext.getBean(SubFundGroupService.class).getContractsAndGrantsDenotingAttributeLabel(), SpringContext.getBean(SubFundGroupService.class).getContractsAndGrantsDenotingValueForMessage() }); 246 success = false; 247 } 248 } 249 250 return success; 251 } 252 } 253 } 254 255 A21SubAccount a21 = newSubAccount.getA21SubAccount(); 256 257 // short circuit if there is no A21SubAccount object at all (ie, null) 258 if (ObjectUtils.isNull(a21)) { 259 return success; 260 } 261 262 // FROM HERE ON IN WE CAN ASSUME THERE IS A VALID A21 SUBACCOUNT OBJECT 263 264 // KFSMI-6848 since there is a ICR Collection Account object, change refresh to perform 265 // manually refresh the a21SubAccount object, as it wont have been 266 // refreshed by the parent, as its updateable 267 // though only refresh if we didn't refresh in the checks above 268 269 if (!a21SubAccountRefreshed) { 270 //preserve the ICRAccounts before refresh to prevent the list from dropping 271 List<A21IndirectCostRecoveryAccount>icrAccounts =a21.getA21IndirectCostRecoveryAccounts(); 272 a21.refresh(); 273 a21.setA21IndirectCostRecoveryAccounts(icrAccounts); 274 275 } 276 277 // C&G A21 Type field must be in the allowed values 278 if (!OLEConstants.SubAccountType.ELIGIBLE_SUB_ACCOUNT_TYPE_CODES.contains(a21.getSubAccountTypeCode())) { 279 putFieldError("a21SubAccount.subAccountTypeCode", OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_INVALI_SUBACCOUNT_TYPE_CODES, OLEConstants.SubAccountType.ELIGIBLE_SUB_ACCOUNT_TYPE_CODES.toString()); 280 success &= false; 281 } 282 283 // get a convenience reference to this code 284 String cgA21TypeCode = a21.getSubAccountTypeCode(); 285 286 // if this is a Cost Sharing SubAccount, run the Cost Sharing rules 287 if (OLEConstants.SubAccountType.COST_SHARE.trim().equalsIgnoreCase(StringUtils.trim(cgA21TypeCode))) { 288 success &= checkCgCostSharingRules(); 289 } 290 291 // if this is an ICR subaccount, run the ICR rules 292 if (OLEConstants.SubAccountType.EXPENSE.trim().equals(StringUtils.trim(cgA21TypeCode))) { 293 success &= checkCgIcrRules(); 294 } 295 296 return success; 297 } 298 299 /** 300 * This checks that if the cost share information is filled out that it is valid and exists, or if fields are missing (such as 301 * the chart of accounts code and account number) an error is recorded 302 * 303 * @return true if all cost share fields filled out correctly, false if the chart of accounts code and account number for cost 304 * share are missing 305 */ 306 protected boolean checkCgCostSharingRules() { 307 308 boolean success = true; 309 boolean allFieldsSet = false; 310 311 A21SubAccount a21 = newSubAccount.getA21SubAccount(); 312 313 // check to see if all required fields are set 314 if (StringUtils.isNotEmpty(a21.getCostShareChartOfAccountCode()) && StringUtils.isNotEmpty(a21.getCostShareSourceAccountNumber())) { 315 allFieldsSet = true; 316 } 317 318 // Cost Sharing COA Code and Cost Sharing Account Number are required 319 success &= checkEmptyBOField("a21SubAccount.costShareChartOfAccountCode", a21.getCostShareChartOfAccountCode(), "Cost Share Chart of Accounts Code"); 320 success &= checkEmptyBOField("a21SubAccount.costShareSourceAccountNumber", a21.getCostShareSourceAccountNumber(), "Cost Share AccountNumber"); 321 322 // existence test on Cost Share Account 323 if (allFieldsSet) { 324 if (ObjectUtils.isNull(a21.getCostShareAccount())) { 325 putFieldError("a21SubAccount.costShareSourceAccountNumber", OLEKeyConstants.ERROR_EXISTENCE, getDisplayName("a21SubAccount.costShareSourceAccountNumber")); 326 success &= false; 327 } 328 } 329 330 // existence test on Cost Share SubAccount 331 if (allFieldsSet && StringUtils.isNotBlank(a21.getCostShareSourceSubAccountNumber())) { 332 if (ObjectUtils.isNull(a21.getCostShareSourceSubAccount())) { 333 putFieldError("a21SubAccount.costShareSourceSubAccountNumber", OLEKeyConstants.ERROR_EXISTENCE, getDisplayName("a21SubAccount.costShareSourceSubAccountNumber")); 334 success &= false; 335 } 336 } 337 338 // Cost Sharing Account may not be for contracts and grants 339 if (ObjectUtils.isNotNull(a21.getCostShareAccount())) { 340 if (ObjectUtils.isNotNull(a21.getCostShareAccount().getSubFundGroup())) { 341 if (a21.getCostShareAccount().isForContractsAndGrants()) { 342 putFieldError("a21SubAccount.costShareSourceAccountNumber", OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_COST_SHARE_ACCOUNT_MAY_NOT_BE_CG_FUNDGROUP, new String[] { SpringContext.getBean(SubFundGroupService.class).getContractsAndGrantsDenotingAttributeLabel(), SpringContext.getBean(SubFundGroupService.class).getContractsAndGrantsDenotingValueForMessage() }); 343 success &= false; 344 } 345 } 346 } 347 348 // The ICR fields must be empty if the sub-account type code is for cost sharing 349 if (checkCgIcrIsEmpty() == false) { 350 putFieldError("a21SubAccount.indirectCostRecoveryTypeCode", OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_ICR_SECTION_INVALID, a21.getSubAccountTypeCode()); 351 success &= false; 352 } 353 354 return success; 355 } 356 357 /** 358 * This checks that if the ICR information is entered that it is valid for this fiscal year and that all of its fields are valid 359 * as well (such as account) 360 * 361 * @return true if the ICR information is filled in and it is valid 362 */ 363 protected boolean checkCgIcrRules() { 364 A21SubAccount a21 = newSubAccount.getA21SubAccount(); 365 if(ObjectUtils.isNull(a21)) { 366 return true; 367 } 368 369 boolean success = true; 370 371 // existence check for ICR Type Code 372 if (StringUtils.isNotEmpty(a21.getIndirectCostRecoveryTypeCode())) { 373 if (ObjectUtils.isNull(a21.getIndirectCostRecoveryType())) { 374 putFieldError("a21SubAccount.indirectCostRecoveryTypeCode", OLEKeyConstants.ERROR_EXISTENCE, "ICR Type Code: " + a21.getIndirectCostRecoveryTypeCode()); 375 success = false; 376 } 377 } 378 379 // existence check for Financial Series ID 380 if (StringUtils.isNotEmpty(a21.getFinancialIcrSeriesIdentifier())) { 381 String fiscalYear = StringUtils.EMPTY + SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear(); 382 String icrSeriesId = a21.getFinancialIcrSeriesIdentifier(); 383 384 Map<String, String> pkMap = new HashMap<String, String>(); 385 pkMap.put(OLEPropertyConstants.UNIVERSITY_FISCAL_YEAR, fiscalYear); 386 pkMap.put(OLEPropertyConstants.FINANCIAL_ICR_SERIES_IDENTIFIER, icrSeriesId); 387 Collection<IndirectCostRecoveryRateDetail> icrRateDetails = getBoService().findMatching(IndirectCostRecoveryRateDetail.class, pkMap); 388 389 if (ObjectUtils.isNull(icrRateDetails) || icrRateDetails.isEmpty()) { 390 String label = SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(A21SubAccount.class, OLEPropertyConstants.FINANCIAL_ICR_SERIES_IDENTIFIER); 391 putFieldError(OLEPropertyConstants.A21_SUB_ACCOUNT + "." + OLEPropertyConstants.FINANCIAL_ICR_SERIES_IDENTIFIER, OLEKeyConstants.ERROR_EXISTENCE, label + " (" + icrSeriesId + ")"); 392 success = false; 393 } 394 else { 395 for(IndirectCostRecoveryRateDetail icrRateDetail : icrRateDetails) { 396 if(ObjectUtils.isNull(icrRateDetail.getIndirectCostRecoveryRate())){ 397 putFieldError(OLEPropertyConstants.A21_SUB_ACCOUNT + "." + OLEPropertyConstants.FINANCIAL_ICR_SERIES_IDENTIFIER, OLEKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_RATE_NOT_FOUND, new String[]{fiscalYear, icrSeriesId}); 398 success = false; 399 break; 400 } 401 } 402 } 403 } 404 405 // existence check for ICR Account 406 for (A21IndirectCostRecoveryAccount account : a21.getA21ActiveIndirectCostRecoveryAccounts()){ 407 if (StringUtils.isNotBlank(account.getIndirectCostRecoveryAccountNumber()) 408 && StringUtils.isNotBlank(account.getIndirectCostRecoveryFinCoaCode())){ 409 if(ObjectUtils.isNull(account.getIndirectCostRecoveryAccount())){ 410 putFieldError(OLEPropertyConstants.A21INDIRECT_COST_RECOVERY_ACCOUNTS, OLEKeyConstants.ERROR_EXISTENCE, "ICR Account: " + account.getIndirectCostRecoveryFinCoaCode() + "-" + account.getIndirectCostRecoveryAccountNumber()); 411 success = false; 412 break; 413 } 414 } 415 } 416 417 // The cost sharing fields must be empty if the sub-account type code is for ICR 418 if (checkCgCostSharingIsEmpty() == false) { 419 putFieldError("a21SubAccount.costShareChartOfAccountCode", OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_COST_SHARE_SECTION_INVALID, a21.getSubAccountTypeCode()); 420 421 success &= false; 422 } 423 424 return success; 425 } 426 427 /** 428 * This method tests if all fields in the Cost Sharing section are empty. 429 * 430 * @return true if the cost sharing values passed in are empty, otherwise false. 431 */ 432 protected boolean checkCgCostSharingIsEmpty() { 433 boolean success = true; 434 435 A21SubAccount newA21SubAccount = newSubAccount.getA21SubAccount(); 436 if (ObjectUtils.isNotNull(newA21SubAccount)) { 437 success &= StringUtils.isEmpty(newA21SubAccount.getCostShareChartOfAccountCode()); 438 success &= StringUtils.isEmpty(newA21SubAccount.getCostShareSourceAccountNumber()); 439 success &= StringUtils.isEmpty(newA21SubAccount.getCostShareSourceSubAccountNumber()); 440 } 441 442 return success; 443 } 444 445 /** 446 * This method tests if all fields in the ICR section are empty. 447 * 448 * @return true if the ICR values passed in are empty, otherwise false. 449 */ 450 protected boolean checkCgIcrIsEmpty() { 451 boolean success = true; 452 453 A21SubAccount newA21SubAccount = newSubAccount.getA21SubAccount(); 454 if (ObjectUtils.isNotNull(newA21SubAccount)) { 455 success &= StringUtils.isEmpty(newA21SubAccount.getFinancialIcrSeriesIdentifier()); 456 457 success &= checkICRCollectionExist(false); 458 success &= StringUtils.isEmpty(newA21SubAccount.getIndirectCostRecoveryTypeCode()); 459 // this is a boolean, so create any value if set to true, meaning a user checked the box, otherwise assume it's empty 460 success &= StringUtils.isEmpty(newA21SubAccount.getOffCampusCode() ? "1" : ""); 461 } 462 463 return success; 464 } 465 466 /** 467 * This method tests the value entered, and if there is anything there it logs a new error, and returns false. 468 * 469 * @param value - String value to be tested 470 * @param fieldName - name of the field being tested 471 * @return false if there is any value in value, otherwise true 472 */ 473 protected boolean disallowAnyValues(String value, String fieldName) { 474 if (StringUtils.isNotEmpty(value)) { 475 putFieldError(fieldName, OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_NOT_AUTHORIZED_ENTER_CG_FIELDS, getDisplayName(fieldName)); 476 return false; 477 } 478 return true; 479 } 480 481 /** 482 * This method tests the two values entered, and if there is any change between the two, it logs an error, and returns false. 483 * Note that the comparison is done after trimming both leading and trailing whitespace from both strings, and then doing a 484 * case-insensitive comparison. 485 * 486 * @param oldValue - the original String value of the field 487 * @param newValue - the new String value of the field 488 * @param fieldName - name of the field being tested 489 * @return false if there is any difference between the old and new, true otherwise 490 */ 491 protected boolean disallowChangedValues(String oldValue, String newValue, String fieldName) { 492 493 if (isFieldValueChanged(oldValue, newValue)) { 494 putFieldError(fieldName, OLEKeyConstants.ERROR_DOCUMENT_SUBACCTMAINT_NOT_AUTHORIZED_CHANGE_CG_FIELDS, getDisplayName(fieldName)); 495 return false; 496 } 497 return true; 498 } 499 500 /** 501 * This compares two string values to see if the newValue has changed from the oldValue 502 * 503 * @param oldValue - original value 504 * @param newValue - new value 505 * @return true if the two fields are different from each other 506 */ 507 protected boolean isFieldValueChanged(String oldValue, String newValue) { 508 509 if (StringUtils.isBlank(oldValue) && StringUtils.isBlank(newValue)) { 510 return false; 511 } 512 513 if (StringUtils.isBlank(oldValue) && StringUtils.isNotBlank(newValue)) { 514 return true; 515 } 516 517 if (StringUtils.isNotBlank(oldValue) && StringUtils.isBlank(newValue)) { 518 return true; 519 } 520 521 if (!oldValue.trim().equalsIgnoreCase(newValue.trim())) { 522 return true; 523 } 524 525 return false; 526 } 527 528 529 /** 530 * This method retrieves the label name for a specific property 531 * 532 * @param propertyName - property to retrieve label for (from the DD) 533 * @return the label 534 */ 535 protected String getDisplayName(String propertyName) { 536 return getDdService().getAttributeLabel(SubAccount.class, propertyName); 537 } 538 539}