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 }