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.ec.document.validation.impl; 017 018 import static org.kuali.kfs.sys.KFSConstants.CurrencyTypeAmounts.HUNDRED_DOLLAR_AMOUNT; 019 import static org.kuali.kfs.sys.businessobject.AccountingLineOverride.CODE.EXPIRED_ACCOUNT; 020 import static org.kuali.kfs.sys.businessobject.AccountingLineOverride.CODE.EXPIRED_ACCOUNT_AND_NON_FRINGE_ACCOUNT_USED; 021 022 import java.util.Arrays; 023 import java.util.List; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.kuali.kfs.coa.businessobject.A21SubAccount; 027 import org.kuali.kfs.coa.businessobject.Account; 028 import org.kuali.kfs.module.ec.EffortConstants; 029 import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail; 030 import org.kuali.kfs.module.ec.document.EffortCertificationDocument; 031 import org.kuali.kfs.module.ec.util.PayrollAmountHolder; 032 import org.kuali.kfs.sys.KFSConstants; 033 import org.kuali.kfs.sys.ObjectUtil; 034 import org.kuali.kfs.sys.context.SpringContext; 035 import org.kuali.kfs.sys.service.UniversityDateService; 036 import org.kuali.rice.kns.service.DictionaryValidationService; 037 import org.kuali.rice.kns.util.GlobalVariables; 038 import org.kuali.rice.kns.util.KualiDecimal; 039 import org.kuali.rice.kns.util.ObjectUtils; 040 041 /** 042 * Provides a set of facilities to determine whether the given Effort Certification Documents or Effort Certification Detail meet 043 * the specified requirements. 044 */ 045 public class EffortCertificationDocumentRuleUtil { 046 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EffortCertificationDocumentRuleUtil.class); 047 048 /** 049 * reset the attribute with the blank value to the default values 050 * 051 * @param detailLine the given detail line 052 */ 053 public static void applyDefaultValues(EffortCertificationDetail detailLine) { 054 055 if (StringUtils.isBlank(detailLine.getSubAccountNumber())) { 056 detailLine.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 057 } 058 059 if (StringUtils.isBlank(detailLine.getCostShareSourceSubAccountNumber())) { 060 detailLine.setCostShareSourceSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 061 } 062 063 if (StringUtils.isBlank(detailLine.getSourceChartOfAccountsCode())) { 064 detailLine.setSourceChartOfAccountsCode(EffortConstants.DASH_CHART_OF_ACCOUNTS_CODE); 065 } 066 067 if (StringUtils.isBlank(detailLine.getSourceAccountNumber())) { 068 detailLine.setSourceAccountNumber(EffortConstants.DASH_ACCOUNT_NUMBER); 069 } 070 071 if (ObjectUtils.isNull(detailLine.getEffortCertificationPayrollAmount())) { 072 detailLine.setEffortCertificationPayrollAmount(KualiDecimal.ZERO); 073 } 074 075 if (ObjectUtils.isNull(detailLine.getEffortCertificationOriginalPayrollAmount())) { 076 detailLine.setEffortCertificationOriginalPayrollAmount(KualiDecimal.ZERO); 077 } 078 079 if (ObjectUtils.isNull(detailLine.getEffortCertificationCalculatedOverallPercent())) { 080 detailLine.setEffortCertificationCalculatedOverallPercent(0); 081 } 082 083 if (ObjectUtils.isNull(detailLine.getEffortCertificationUpdatedOverallPercent())) { 084 detailLine.setEffortCertificationUpdatedOverallPercent(0); 085 } 086 087 UniversityDateService universityDateService = SpringContext.getBean(UniversityDateService.class); 088 detailLine.setUniversityFiscalYear(universityDateService.getCurrentFiscalYear()); 089 } 090 091 /** 092 * determine whether the expired account in the detail line can be used. 093 * 094 * @param detailLine the given detail line 095 * @return true if the expired account in the detail line can be used; otherwise, false 096 */ 097 public static boolean canExpiredAccountBeUsed(EffortCertificationDetail detailLine) { 098 Account account = detailLine.getAccount(); 099 100 boolean canExpiredAccountUsed = true; 101 if (ObjectUtils.isNotNull(account) && account.isExpired()) { 102 String overrideCode = detailLine.getOverrideCode(); 103 canExpiredAccountUsed = Arrays.asList(EXPIRED_ACCOUNT, EXPIRED_ACCOUNT_AND_NON_FRINGE_ACCOUNT_USED).contains(overrideCode); 104 } 105 return canExpiredAccountUsed; 106 } 107 108 /** 109 * determine if the sub account associated with the given detail line is a valid A21 sub account 110 * 111 * @param detailLine the given detail line 112 * @return true if the sub account associated with the given detail line is a valid A21 sub account; otherwise, false 113 */ 114 public static boolean hasA21SubAccount(EffortCertificationDetail detailLine) { 115 String subAccountNumber = detailLine.getSubAccountNumber(); 116 if (KFSConstants.getDashSubAccountNumber().equals(subAccountNumber)) { 117 return false; 118 } 119 return ObjectUtils.isNotNull(detailLine.getSubAccount().getA21SubAccount()); 120 } 121 122 /** 123 * determine if the given detail line is associated with a closed account 124 * 125 * @param detailLine the given detail line 126 * @return true if the given detail line is associated with a closed account; otherwise, false 127 */ 128 public static boolean hasClosedAccount(EffortCertificationDetail detailLine) { 129 return !detailLine.getAccount().isActive(); 130 } 131 132 /** 133 * determine if the given detail line is associated with a contract grant account 134 * 135 * @param detailLine the given detail line 136 * @return true if the given detail line is associated with a contract grant account; otherwise, false 137 */ 138 public static boolean hasContractGrantAccount(EffortCertificationDetail detailLine) { 139 return detailLine.getAccount().isForContractsAndGrants(); 140 } 141 142 /** 143 * determine if the given detail line is associated with a sub account whose type code is in the given list 144 * 145 * @param detailLine the given detail line 146 * @param designatedCostShareSubAccountTypeCode the designated cost share sub account type codes 147 * @return true if the given detail line is associated with a sub account whose type code is in the given list; otherwise, false 148 */ 149 public static boolean hasCostShareSubAccount(EffortCertificationDetail detailLine, List<String> designatedCostShareSubAccountTypeCodes) { 150 if (!hasA21SubAccount(detailLine)) { 151 return false; 152 } 153 154 String costShareSubAccountTypeCode = detailLine.getSubAccount().getA21SubAccount().getSubAccountTypeCode(); 155 return designatedCostShareSubAccountTypeCodes.contains(costShareSubAccountTypeCode); 156 } 157 158 /** 159 * determine if the payroll amount of the given detail line is not negative 160 * 161 * @param detailLine the given detail line 162 * @return true if the payroll amount of the given detail line is not negative; otherwise, false 163 */ 164 public static boolean hasNonnegativePayrollAmount(EffortCertificationDetail detailLine) { 165 KualiDecimal payrollAmount = detailLine.getEffortCertificationPayrollAmount(); 166 167 return ObjectUtils.isNotNull(payrollAmount) && isPayrollAmountNonnegative(payrollAmount); 168 } 169 170 /** 171 * determine if there is a line in the given document that has the same values for the comparable fields as the given detail 172 * line 173 * 174 * @param document the given effort certification document 175 * @param detailLine the given detail line 176 * @param comparableFields the comparable fields 177 * @return true if there is a line in the given document that has the same values for the comparable fields as the given detail 178 * line; otherwise, false 179 */ 180 public static boolean hasSameExistingLine(EffortCertificationDocument document, EffortCertificationDetail detailLine, List<String> comparableFields) { 181 List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines(); 182 183 for (EffortCertificationDetail line : detailLines) { 184 if(detailLine != line && ObjectUtil.equals(line, detailLine, comparableFields)) { 185 return true; 186 } 187 } 188 return false; 189 } 190 191 /** 192 * determine if the given detail line has a valid effort percentage. The percentage should be between 0 and 100. 193 * 194 * @param detailLine the given detail line 195 * @return true if the given detail line has a valid effort percentage; otherwise, false 196 */ 197 public static boolean hasValidEffortPercent(EffortCertificationDetail detailLine) { 198 Integer effortPercent = detailLine.getEffortCertificationUpdatedOverallPercent(); 199 200 return ObjectUtils.isNotNull(effortPercent) && isValidPercent(effortPercent); 201 } 202 203 /** 204 * determine if the fields in the detail line are in the correct formats defined in the data dictionary 205 * 206 * @param detailLine the given detail line 207 * @return true if the fields in the detail line are in the correct formats defined in the data dictionary; otherwise, false 208 */ 209 public static boolean hasValidFormat(EffortCertificationDetail detailLine) { 210 int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount(); 211 SpringContext.getBean(DictionaryValidationService.class).validateBusinessObject(detailLine); 212 int currentErrorCount = GlobalVariables.getMessageMap().getErrorCount(); 213 214 return currentErrorCount == originalErrorCount; 215 } 216 217 /** 218 * determine if there is a change on the payroll amount of the given detail line comparing to its original payroll amount 219 * 220 * @param detailLine the given effort certification detail line 221 * @return true if there is a change on the payroll amount of the given detail line comparing to its original payroll amount 222 */ 223 public static boolean isPayrollAmountChangedFromOriginal(EffortCertificationDetail detailLine) { 224 KualiDecimal payrollAmount = detailLine.getEffortCertificationPayrollAmount(); 225 KualiDecimal originalPayrollAmount = detailLine.getEffortCertificationOriginalPayrollAmount(); 226 KualiDecimal difference = originalPayrollAmount.subtract(payrollAmount); 227 228 return difference.isNonZero(); 229 } 230 231 /** 232 * determine if there is a change on the payroll amount of the given document 233 * 234 * @param document the given effort certification document 235 * @return true if there is the change on the payroll amount of any detail line in the given document 236 */ 237 public static boolean isPayrollAmountChangedFromOriginal(EffortCertificationDocument document) { 238 List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines(); 239 240 for (EffortCertificationDetail line : detailLines) { 241 if (isPayrollAmountChangedFromOriginal(line)) { 242 return true; 243 } 244 } 245 246 return false; 247 } 248 249 /** 250 * determine if there is a change on the payroll amount of the given detail line comparing to its persisted payroll amount 251 * 252 * @param detailLine the given effort certification detail line 253 * @return true if there is a change on the payroll amount of the given detail line comparing to its persisted payroll amount 254 */ 255 public static boolean isPayrollAmountChangedFromPersisted(EffortCertificationDetail detailLine) { 256 KualiDecimal persistedAmount = detailLine.getPersistedPayrollAmount(); 257 KualiDecimal difference = KualiDecimal.ZERO; 258 259 if (ObjectUtils.isNotNull(persistedAmount)) { 260 KualiDecimal payrollAmount = detailLine.getEffortCertificationPayrollAmount(); 261 difference = persistedAmount.subtract(payrollAmount); 262 } 263 264 return difference.isNonZero(); 265 } 266 267 /** 268 * determine if there is a change on the payroll amount of the given document 269 * 270 * @param document the given effort certification document 271 * @return true if there is the change on the payroll amount of any detail line in the given document 272 */ 273 public static boolean isPayrollAmountChangedFromPersisted(EffortCertificationDocument document) { 274 List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines(); 275 276 for (EffortCertificationDetail line : detailLines) { 277 if (isPayrollAmountChangedFromPersisted(line)) { 278 return true; 279 } 280 } 281 282 return false; 283 } 284 285 /** 286 * determine if there is a change on the payroll amount of the given detail line comparing to its persisted payroll amount 287 * 288 * @param detailLine the given effort certification detail line 289 * @return true if there is a change on the payroll amount of the given detail line comparing to its persisted payroll amount 290 */ 291 public static boolean isEffortPercentChangedFromPersisted(EffortCertificationDetail detailLine) { 292 Integer persistedAmount = detailLine.getPersistedEffortPercent(); 293 Integer effortPercent = detailLine.getEffortCertificationUpdatedOverallPercent(); 294 295 return !persistedAmount.equals(effortPercent); 296 } 297 298 /** 299 * determine if there is a change on the payroll amount of the given document 300 * 301 * @param document the given effort certification document 302 * @return true if there is the change on the payroll amount of any detail line in the given document 303 */ 304 public static boolean isEffortPercentChangedFromPersisted(EffortCertificationDocument document) { 305 List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines(); 306 307 for (EffortCertificationDetail line : detailLines) { 308 if (isEffortPercentChangedFromPersisted(line)) { 309 return true; 310 } 311 } 312 313 return false; 314 } 315 316 /** 317 * determine if the given payroll amount is greater than and equal to 0 318 * 319 * @param payrollAmount the given payroll amount 320 * @return true if the given payroll amount is greater than and equal to 0; otherwise, false 321 */ 322 public static boolean isPayrollAmountNonnegative(KualiDecimal payrollAmount) { 323 return payrollAmount.isGreaterEqual(KualiDecimal.ZERO); 324 } 325 326 /** 327 * determine if original effort percent is same as the current effort percent for the given detail line 328 * @param detailLine the given effort certification detail line 329 * @return true if original effort percent same as current effort percent 330 */ 331 332 public static boolean isOriginalEffortPercentSameAsCurrentEffortPercent(Integer originalEffortPercent, Integer effortPercent) { 333 return originalEffortPercent.equals(effortPercent); 334 } 335 336 /** 337 * determine if the change on the payroll amount of the given detail line exceeds the specified limit 338 * 339 * @param detailLine the given effort certification detail line 340 * @param limitOfLinePayrollAmountChange the specified upper bound limit 341 * @return true if the change on the payroll amount of the given detail line exceeds the specified limit; otherwise, false 342 */ 343 public static boolean isPayrollAmountOverChanged(EffortCertificationDetail detailLine, KualiDecimal originalTotalAmount, double limitOfLinePayrollAmountChange) { 344 KualiDecimal payrollAmount = detailLine.getEffortCertificationPayrollAmount(); 345 KualiDecimal originalPayrollAmount = detailLine.getEffortCertificationOriginalPayrollAmount(); 346 347 KualiDecimal difference = KualiDecimal.ZERO; 348 349 Integer originalEffortPercent = detailLine.getEffortCertificationCalculatedOverallPercent(); 350 Integer effortPercent = detailLine.getEffortCertificationUpdatedOverallPercent(); 351 if (isOriginalEffortPercentSameAsCurrentEffortPercent(originalEffortPercent, effortPercent)) { 352 difference = originalPayrollAmount.subtract(payrollAmount).multiply(HUNDRED_DOLLAR_AMOUNT).abs(); 353 354 return difference.divide(originalTotalAmount).doubleValue() > limitOfLinePayrollAmountChange * HUNDRED_DOLLAR_AMOUNT.intValue(); 355 } 356 357 return false; 358 } 359 360 /** 361 * determine if there is a change on the payroll amount of a detail line that exceeds the specified limit 362 * 363 * @param document the given effort certification document 364 * @param limitOfLinePayrollAmountChange the specified upper bound limit 365 * @return true if the change on the payroll amount of any detail line exceeds the specified limit; otherwise, false 366 */ 367 public static boolean isPayrollAmountOverChanged(EffortCertificationDocument document, double limitOfLinePayrollAmountChange) { 368 List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines(); 369 KualiDecimal originalTotalAmount = document.getTotalOriginalPayrollAmount(); 370 371 for (EffortCertificationDetail line : detailLines) { 372 if (isPayrollAmountOverChanged(line, originalTotalAmount, limitOfLinePayrollAmountChange)) { 373 return true; 374 } 375 } 376 377 return false; 378 } 379 380 /** 381 * detrmine if the total effort percent of the given document is 100 382 * 383 * @param document the given effort certification document 384 * @return true if the total effort percent of the given document is 100 385 */ 386 public static boolean isTotalEffortPercentageAs100(EffortCertificationDocument document) { 387 return document.getTotalEffortPercent() == 100; 388 } 389 390 /** 391 * determine if the change on the total payroll amount exceeds the specified limit 392 * 393 * @param document the given effort certification document 394 * @param limitOfTotalPayrollAmountChange the specified upper bound limit 395 * @return true if the change on the total payroll amount exceeds the specified limit; otherwise, false 396 */ 397 public static boolean isTotalPayrollAmountOverChanged(EffortCertificationDocument document, double limitOfTotalPayrollAmountChange) { 398 KualiDecimal totalPayrollAmount = document.getTotalPayrollAmount(); 399 KualiDecimal totalOriginalPayrollAmount = document.getTotalOriginalPayrollAmount(); 400 KualiDecimal difference = totalOriginalPayrollAmount.subtract(totalPayrollAmount).abs(); 401 402 return difference.doubleValue() > limitOfTotalPayrollAmountChange; 403 } 404 405 /** 406 * determine if the given percent is between 0 and 100. 407 * 408 * @param percent the given percent 409 * @return true if the given percent is between 0 and 100; otherwise, false 410 */ 411 public static boolean isValidPercent(Integer percent) { 412 return percent >= 0 && percent <= 100; 413 } 414 415 /** 416 * update the information of the source attributes for the given detail line 417 * 418 * @param detailLine the given detail line 419 */ 420 public static void updateSourceAccountInformation(EffortCertificationDetail detailLine) { 421 A21SubAccount a21SubAccount = detailLine.getSubAccount().getA21SubAccount(); 422 423 if (ObjectUtils.isNotNull(a21SubAccount)) { 424 detailLine.setSourceChartOfAccountsCode(a21SubAccount.getCostShareChartOfAccountCode()); 425 detailLine.setSourceAccountNumber(a21SubAccount.getCostShareSourceAccountNumber()); 426 detailLine.setCostShareSourceSubAccountNumber(a21SubAccount.getCostShareSourceSubAccountNumber()); 427 } 428 } 429 430 /** 431 * determine if there is a line associated with the given document 432 * 433 * @param document the given effort certification document 434 * @return true if there is a line associated with the given document; otherwise, false 435 */ 436 public static boolean hasDetailLine(EffortCertificationDocument document) {; 437 List<EffortCertificationDetail> detailLines = document.getEffortCertificationDetailLines(); 438 439 return detailLines != null && !detailLines.isEmpty(); 440 } 441 442 }