001/* 002 * Copyright 2007 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.module.purap.service.impl; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.ole.module.purap.PurapConstants; 020import org.kuali.ole.module.purap.PurapConstants.PurapDocTypeCodes; 021import org.kuali.ole.module.purap.PurapKeyConstants; 022import org.kuali.ole.module.purap.PurapParameterConstants.NRATaxParameters; 023import org.kuali.ole.module.purap.PurapPropertyConstants; 024import org.kuali.ole.module.purap.businessobject.*; 025import org.kuali.ole.module.purap.dataaccess.PurApAccountingDao; 026import org.kuali.ole.module.purap.document.InvoiceDocument; 027import org.kuali.ole.module.purap.document.PaymentRequestDocument; 028import org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument; 029import org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocumentBase; 030import org.kuali.ole.module.purap.document.service.PurapService; 031import org.kuali.ole.module.purap.service.PurapAccountingService; 032import org.kuali.ole.module.purap.util.PurApItemUtils; 033import org.kuali.ole.module.purap.util.PurApObjectUtils; 034import org.kuali.ole.module.purap.util.SummaryAccount; 035import org.kuali.ole.module.purap.util.UseTaxContainer; 036import org.kuali.ole.select.businessobject.*; 037import org.kuali.ole.select.document.OleInvoiceDocument; 038import org.kuali.ole.select.document.OlePaymentRequestDocument; 039import org.kuali.ole.select.document.OleVendorCreditMemoDocument; 040import org.kuali.ole.sys.businessobject.AccountingLineBase; 041import org.kuali.ole.sys.businessobject.SourceAccountingLine; 042import org.kuali.ole.sys.context.SpringContext; 043import org.kuali.ole.sys.service.NonTransactional; 044import org.kuali.rice.core.api.util.type.KualiDecimal; 045import org.kuali.rice.coreservice.framework.parameter.ParameterService; 046import org.kuali.rice.krad.service.BusinessObjectService; 047import org.kuali.rice.krad.service.KualiRuleService; 048import org.kuali.rice.krad.util.GlobalVariables; 049import org.kuali.rice.krad.util.ObjectUtils; 050 051import java.math.BigDecimal; 052import java.util.*; 053 054/** 055 * Contains a number of helper methods to deal with accounts on Purchasing Accounts Payable Documents 056 */ 057 058@NonTransactional 059public class PurapAccountingServiceImpl implements PurapAccountingService { 060 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurapAccountingServiceImpl.class); 061 062 protected static final BigDecimal ONE_HUNDRED = new BigDecimal(100); 063 protected static final int SCALE = 340; 064 protected static final int BIG_DECIMAL_ROUNDING_MODE = BigDecimal.ROUND_HALF_UP; 065 protected static final int BIG_DECIMAL_SCALE = 2; 066 067 // local constants 068 protected static final Boolean ITEM_TYPES_INCLUDED_VALUE = Boolean.TRUE; 069 ; 070 protected static final Boolean ITEM_TYPES_EXCLUDED_VALUE = Boolean.FALSE; 071 protected static final Boolean ZERO_TOTALS_RETURNED_VALUE = Boolean.TRUE; 072 protected static final Boolean ZERO_TOTALS_NOT_RETURNED_VALUE = Boolean.FALSE; 073 protected static final Boolean ALTERNATE_AMOUNT_USED = Boolean.TRUE; 074 protected static final Boolean ALTERNATE_AMOUNT_NOT_USED = Boolean.FALSE; 075 protected static final Boolean USE_TAX_INCLUDED = Boolean.TRUE; 076 protected static final Boolean USE_TAX_EXCLUDED = Boolean.FALSE; 077 078 protected ParameterService parameterService; 079 protected PurapService purapService; 080 protected PurApAccountingDao purApAccountingDao; 081 protected BusinessObjectService businessObjectService; 082 private OlePaymentRequestDocument olePaymentRequestDocument; 083 084 /** 085 * gets the lowest possible number for rounding, it works for ROUND_HALF_UP 086 * 087 * @return a BigDecimal representing the lowest possible number for rounding 088 */ 089 protected BigDecimal getLowestPossibleRoundUpNumber() { 090 BigDecimal startingDigit = new BigDecimal(0.5); 091 if (SCALE != 0) { 092 startingDigit = startingDigit.movePointLeft(SCALE); 093 } 094 return startingDigit; 095 } 096 097 /** 098 * Helper method to log and throw an error 099 * 100 * @param methodName the method it's coming from 101 * @param errorMessage the actual error 102 */ 103 protected void throwRuntimeException(String methodName, String errorMessage) { 104 LOG.error(methodName + " " + errorMessage); 105 throw new RuntimeException(errorMessage); 106 } 107 108 /** 109 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateAccountDistributionForProration(java.util.List, 110 * org.kuali.rice.kns.util.KualiDecimal, java.lang.Integer) 111 * @deprecated 112 */ 113 @Deprecated 114 @Override 115 public List<PurApAccountingLine> generateAccountDistributionForProration(List<SourceAccountingLine> accounts, KualiDecimal totalAmount, Integer percentScale) { 116 return null; 117 } 118 119 /** 120 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateAccountDistributionForProration(java.util.List, 121 * org.kuali.rice.kns.util.KualiDecimal, java.lang.Integer) 122 */ 123 @Override 124 public List<PurApAccountingLine> generateAccountDistributionForProration(List<SourceAccountingLine> accounts, KualiDecimal totalAmount, Integer percentScale, Class clazz) { 125 String methodName = "generateAccountDistributionForProration()"; 126 if (LOG.isDebugEnabled()) { 127 LOG.debug(methodName + " started"); 128 } 129 List<PurApAccountingLine> newAccounts = new ArrayList(); 130 131 if (totalAmount.isZero()) { 132 throwRuntimeException(methodName, "Purchasing/Accounts Payable account distribution for proration does not allow zero dollar total."); 133 } 134 135 BigDecimal percentTotal = BigDecimal.ZERO; 136 BigDecimal totalAmountBigDecimal = totalAmount.bigDecimalValue(); 137 for (SourceAccountingLine accountingLine : accounts) { 138 KualiDecimal amt = KualiDecimal.ZERO; 139 if (ObjectUtils.isNotNull(accountingLine.getAmount())) { 140 amt = accountingLine.getAmount(); 141 } 142 143 if (LOG.isDebugEnabled()) { 144 LOG.debug(methodName + " " + accountingLine.getAccountNumber() + " " + amt + "/" + totalAmountBigDecimal); 145 } 146 BigDecimal pct = amt.bigDecimalValue().divide(totalAmountBigDecimal, percentScale, BIG_DECIMAL_ROUNDING_MODE); 147 pct = pct.stripTrailingZeros().multiply(ONE_HUNDRED); 148 149 if (LOG.isDebugEnabled()) { 150 LOG.debug(methodName + " pct = " + pct + " (trailing zeros removed)"); 151 } 152 153 BigDecimal lowestPossible = this.getLowestPossibleRoundUpNumber(); 154 if (lowestPossible.compareTo(pct) <= 0) { 155 PurApAccountingLine newAccountingLine; 156 newAccountingLine = null; 157 158 try { 159 newAccountingLine = (PurApAccountingLine) clazz.newInstance(); 160 } catch (InstantiationException e) { 161 e.printStackTrace(); 162 } catch (IllegalAccessException e) { 163 e.printStackTrace(); 164 } 165 166 PurApObjectUtils.populateFromBaseClass(AccountingLineBase.class, accountingLine, newAccountingLine); 167 newAccountingLine.setAccountLinePercent(pct); 168 if (LOG.isDebugEnabled()) { 169 LOG.debug(methodName + " adding " + newAccountingLine.getAccountLinePercent()); 170 } 171 newAccounts.add(newAccountingLine); 172 percentTotal = percentTotal.add(newAccountingLine.getAccountLinePercent()); 173 if (LOG.isDebugEnabled()) { 174 LOG.debug(methodName + " total = " + percentTotal); 175 } 176 } 177 } 178 179 if ((percentTotal.compareTo(BigDecimal.ZERO)) == 0) { 180 /* 181 * This means there are so many accounts or so strange a distribution that we can't round properly... not sure of viable 182 * solution 183 */ 184 throwRuntimeException(methodName, "Can't round properly due to number of accounts"); 185 } 186 187 // Now deal with rounding 188 if ((ONE_HUNDRED.compareTo(percentTotal)) < 0) { 189 /* 190 * The total percent is greater than one hundred Here we find the account that occurs latest in our list with a percent 191 * that is higher than the difference and we subtract off the difference 192 */ 193 BigDecimal difference = percentTotal.subtract(ONE_HUNDRED); 194 if (LOG.isDebugEnabled()) { 195 LOG.debug(methodName + " Rounding up by " + difference); 196 } 197 198 boolean foundAccountToUse = false; 199 int currentNbr = newAccounts.size() - 1; 200 while (currentNbr >= 0) { 201 PurApAccountingLine potentialSlushAccount = newAccounts.get(currentNbr); 202 203 BigDecimal linePercent = BigDecimal.ZERO; 204 if (ObjectUtils.isNotNull(potentialSlushAccount.getAccountLinePercent())) { 205 linePercent = potentialSlushAccount.getAccountLinePercent(); 206 } 207 208 if ((difference.compareTo(linePercent)) < 0) { 209 // the difference amount is less than the current accounts percent... use this account 210 // the 'potentialSlushAccount' technically is now the true 'Slush Account' 211 potentialSlushAccount.setAccountLinePercent(linePercent.subtract(difference).movePointLeft(2).stripTrailingZeros().movePointRight(2)); 212 foundAccountToUse = true; 213 break; 214 } 215 currentNbr--; 216 } 217 218 if (!foundAccountToUse) { 219 /* 220 * We could not find any account in our list where the percent of that account was greater than that of the 221 * difference... doing so on just any account could result in a negative percent value 222 */ 223 throwRuntimeException(methodName, "Can't round properly due to math calculation error"); 224 } 225 226 } else if ((ONE_HUNDRED.compareTo(percentTotal)) > 0) { 227 /* 228 * The total percent is less than one hundred Here we find the last account in our list and add the remaining required 229 * percent to its already calculated percent 230 */ 231 BigDecimal difference = ONE_HUNDRED.subtract(percentTotal); 232 if (LOG.isDebugEnabled()) { 233 LOG.debug(methodName + " Rounding down by " + difference); 234 } 235 PurApAccountingLine slushAccount = newAccounts.get(newAccounts.size() - 1); 236 237 BigDecimal slushLinePercent = BigDecimal.ZERO; 238 if (ObjectUtils.isNotNull(slushAccount.getAccountLinePercent())) { 239 slushLinePercent = slushAccount.getAccountLinePercent(); 240 } 241 242 slushAccount.setAccountLinePercent(slushLinePercent.add(difference).movePointLeft(2).stripTrailingZeros().movePointRight(2)); 243 } 244 if (LOG.isDebugEnabled()) { 245 LOG.debug(methodName + " ended"); 246 } 247 return newAccounts; 248 } 249 250 /** 251 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateAccountDistributionForProrationWithZeroTotal(java.util.List, 252 * java.lang.Integer) 253 */ 254 @Override 255 public List<PurApAccountingLine> generateAccountDistributionForProrationWithZeroTotal(PurchasingAccountsPayableDocument purapDoc) { 256 String methodName = "generateAccountDistributionForProrationWithZeroTotal()"; 257 if (LOG.isDebugEnabled()) { 258 LOG.debug(methodName + " started"); 259 } 260 261 List<PurApAccountingLine> accounts = generatePercentSummary(purapDoc); 262 263 // find the total percent and strip trailing zeros 264 BigDecimal totalPercentValue = BigDecimal.ZERO; 265 for (PurApAccountingLine accountingLine : accounts) { 266 BigDecimal linePercent = BigDecimal.ZERO; 267 if (ObjectUtils.isNotNull(accountingLine.getAccountLinePercent())) { 268 linePercent = accountingLine.getAccountLinePercent(); 269 } 270 271 totalPercentValue = totalPercentValue.add(linePercent).movePointLeft(2).stripTrailingZeros().movePointRight(2); 272 } 273 274 if ((BigDecimal.ZERO.compareTo(totalPercentValue.remainder(ONE_HUNDRED))) != 0) { 275 throwRuntimeException(methodName, "Invalid Percent Total of '" + totalPercentValue + "' does not allow for account distribution (must be multiple of 100)"); 276 } 277 278 List newAccounts = new ArrayList(); 279 BigDecimal logDisplayOnlyTotal = BigDecimal.ZERO; 280 BigDecimal percentUsed = BigDecimal.ZERO; 281 int accountListSize = accounts.size(); 282 int i = 0; 283 for (PurApAccountingLine accountingLine : accounts) { 284 i++; 285 BigDecimal percentToUse = BigDecimal.ZERO; 286 KualiDecimal amt = KualiDecimal.ZERO; 287 288 if (ObjectUtils.isNotNull(accountingLine.getAmount())) { 289 amt = accountingLine.getAmount(); 290 } 291 292 if (LOG.isDebugEnabled()) { 293 LOG.debug(methodName + " " + accountingLine.getChartOfAccountsCode() + "-" + accountingLine.getAccountNumber() + " " + amt + "/" + percentToUse); 294 } 295 296 // if it's the last account make up the leftover percent 297 BigDecimal acctPercent = BigDecimal.ZERO; 298 if (ObjectUtils.isNotNull(accountingLine.getAccountLinePercent())) { 299 acctPercent = accountingLine.getAccountLinePercent(); 300 } 301 302 if ((i != accountListSize) || (accountListSize == 1)) { 303 // this account is not the last account or there is only one account 304 percentToUse = (acctPercent.divide(totalPercentValue, SCALE, BIG_DECIMAL_ROUNDING_MODE)).multiply(ONE_HUNDRED); 305 percentUsed = percentUsed.add(((acctPercent.divide(totalPercentValue, SCALE, BIG_DECIMAL_ROUNDING_MODE))).multiply(ONE_HUNDRED)); 306 } else { 307 // this account is the last account so we have to makeup whatever is left out of 100 308 percentToUse = ONE_HUNDRED.subtract(percentUsed); 309 } 310 311 PurApAccountingLine newAccountingLine = accountingLine.createBlankAmountsCopy(); 312 if (LOG.isDebugEnabled()) { 313 LOG.debug(methodName + " pct = " + percentToUse); 314 } 315 newAccountingLine.setAccountLinePercent(percentToUse.setScale(acctPercent.scale(), BIG_DECIMAL_ROUNDING_MODE)); 316 if (LOG.isDebugEnabled()) { 317 LOG.debug(methodName + " adding " + newAccountingLine.getAccountLinePercent()); 318 } 319 newAccounts.add(newAccountingLine); 320 logDisplayOnlyTotal = logDisplayOnlyTotal.add(newAccountingLine.getAccountLinePercent()); 321 if (LOG.isDebugEnabled()) { 322 LOG.debug(methodName + " total = " + logDisplayOnlyTotal); 323 } 324 } 325 if (LOG.isDebugEnabled()) { 326 LOG.debug(methodName + " ended"); 327 } 328 return newAccounts; 329 } 330 331 /** 332 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummary(java.util.List) 333 */ 334 @Override 335 public List<SourceAccountingLine> generateSummary(List<PurApItem> items) { 336 String methodName = "generateSummary()"; 337 if (LOG.isDebugEnabled()) { 338 LOG.debug(methodName + " started"); 339 } 340 List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 341 if (LOG.isDebugEnabled()) { 342 LOG.debug(methodName + " ended"); 343 } 344 return returnList; 345 } 346 347 @Override 348 public List<SourceAccountingLine> generateSummaryTaxableAccounts(List<PurApItem> items) { 349 String methodName = "generateSummary()"; 350 if (LOG.isDebugEnabled()) { 351 LOG.debug(methodName + " started"); 352 } 353 List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, true); 354 if (LOG.isDebugEnabled()) { 355 LOG.debug(methodName + " ended"); 356 } 357 return returnList; 358 } 359 360 /** 361 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryAccounts(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument) 362 */ 363 @Override 364 public List<SummaryAccount> generateSummaryAccounts(PurchasingAccountsPayableDocument document) { 365 // always update the amounts first 366 updateAccountAmounts(document); 367 return generateSummaryAccounts(document.getItems(), ZERO_TOTALS_RETURNED_VALUE, USE_TAX_INCLUDED); 368 } 369 370 371 /** 372 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryAccountsWithNoZeroTotals(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument) 373 */ 374 @Override 375 public List<SummaryAccount> generateSummaryAccountsWithNoZeroTotals(PurchasingAccountsPayableDocument document) { 376 // always update the amounts first 377 updateAccountAmounts(document); 378 return generateSummaryAccounts(document.getItems(), ZERO_TOTALS_NOT_RETURNED_VALUE, USE_TAX_INCLUDED); 379 } 380 381 /** 382 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryAccountsWithNoZeroTotals(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument) 383 */ 384 @Override 385 public List<SummaryAccount> generateSummaryAccountsWithNoZeroTotalsNoUseTax(PurchasingAccountsPayableDocument document) { 386 // always update the amounts first 387 updateAccountAmounts(document); 388 return generateSummaryAccounts(document.getItems(), ZERO_TOTALS_NOT_RETURNED_VALUE, USE_TAX_EXCLUDED); 389 } 390 391 /** 392 * This creates summary accounts based on a list of items. 393 * 394 * @param items a list of PurAp Items. 395 * @return a list of summary accounts. 396 */ 397 protected List<SummaryAccount> generateSummaryAccounts(List<PurApItem> items, Boolean useZeroTotals, Boolean useTaxIncluded) { 398 String methodName = "generateSummaryAccounts()"; 399 List<SummaryAccount> returnList = new ArrayList<SummaryAccount>(); 400 if (LOG.isDebugEnabled()) { 401 LOG.debug(methodName + " started"); 402 } 403 404 List<SourceAccountingLine> sourceLines = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, useZeroTotals, ALTERNATE_AMOUNT_NOT_USED, useTaxIncluded, false); 405 for (SourceAccountingLine sourceAccountingLine : sourceLines) { 406 SummaryAccount summaryAccount = new SummaryAccount(); 407 summaryAccount.setAccount((SourceAccountingLine) ObjectUtils.deepCopy(sourceAccountingLine)); 408 for (PurApItem item : items) { 409 List<PurApAccountingLine> itemAccounts = item.getSourceAccountingLines(); 410 for (PurApAccountingLine purApAccountingLine : itemAccounts) { 411 if (purApAccountingLine.accountStringsAreEqual(summaryAccount.getAccount())) { 412 PurApSummaryItem summaryItem = item.getSummaryItem(); 413 // If the summaryItem is null, it means the item is not eligible to 414 // be displayed in the Account Summary tab. If it's not null then 415 // we'll set the estimatedEncumberanceAmount and add the item to the 416 // summaryAccount list to be displayed in the Account Summary tab. 417 KualiDecimal amt = KualiDecimal.ZERO; 418 if (ObjectUtils.isNotNull(purApAccountingLine.getAmount())) { 419 amt = purApAccountingLine.getAmount(); 420 } 421 if (summaryItem != null) { 422 summaryItem.setEstimatedEncumberanceAmount(amt); 423 summaryAccount.getItems().add(summaryItem); 424 break; 425 } 426 } 427 428 } 429 } 430 returnList.add(summaryAccount); 431 } 432 if (LOG.isDebugEnabled()) { 433 LOG.debug(methodName + " ended"); 434 } 435 return returnList; 436 } 437 438 /** 439 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryWithNoZeroTotals(java.util.List) 440 */ 441 @Override 442 public List<SourceAccountingLine> generateSummaryWithNoZeroTotals(List<PurApItem> items) { 443 String methodName = "generateSummaryWithNoZeroTotals()"; 444 if (LOG.isDebugEnabled()) { 445 LOG.debug(methodName + " started"); 446 } 447 List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 448 if (LOG.isDebugEnabled()) { 449 LOG.debug(methodName + " ended"); 450 } 451 return returnList; 452 } 453 454 /** 455 * calls generateSummary with no use tax included 456 */ 457 @Override 458 public List<SourceAccountingLine> generateSummaryWithNoZeroTotalsNoUseTax(List<PurApItem> items) { 459 String methodName = "generateSummaryWithNoZeroTotalsNoUseTax()"; 460 if (LOG.isDebugEnabled()) { 461 LOG.debug(methodName + " started"); 462 } 463 List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_EXCLUDED, false); 464 if (LOG.isDebugEnabled()) { 465 LOG.debug(methodName + " ended"); 466 } 467 468 return returnList; 469 } 470 471 /** 472 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryWithNoZeroTotalsUsingAlternateAmount(java.util.List) 473 */ 474 @Override 475 public List<SourceAccountingLine> generateSummaryWithNoZeroTotalsUsingAlternateAmount(List<PurApItem> items) { 476 String methodName = "generateSummaryWithNoZeroTotals()"; 477 if (LOG.isDebugEnabled()) { 478 LOG.debug(methodName + " started"); 479 } 480 List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_USED, USE_TAX_INCLUDED, false); 481 if (LOG.isDebugEnabled()) { 482 LOG.debug(methodName + " ended"); 483 } 484 return returnList; 485 } 486 487 /** 488 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryExcludeItemTypes(java.util.List, java.util.Set) 489 */ 490 @Override 491 public List<SourceAccountingLine> generateSummaryExcludeItemTypes(List<PurApItem> items, Set excludedItemTypeCodes) { 492 String methodName = "generateSummaryExcludeItemTypes()"; 493 if (LOG.isDebugEnabled()) { 494 LOG.debug(methodName + " started"); 495 } 496 List<SourceAccountingLine> returnList = generateAccountSummary(items, excludedItemTypeCodes, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 497 if (LOG.isDebugEnabled()) { 498 LOG.debug(methodName + " ended"); 499 } 500 return returnList; 501 } 502 503 /** 504 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryIncludeItemTypesAndNoZeroTotals(java.util.List, 505 * java.util.Set) 506 */ 507 @Override 508 public List<SourceAccountingLine> generateSummaryIncludeItemTypesAndNoZeroTotals(List<PurApItem> items, Set includedItemTypeCodes) { 509 String methodName = "generateSummaryExcludeItemTypesAndNoZeroTotals()"; 510 if (LOG.isDebugEnabled()) { 511 LOG.debug(methodName + " started"); 512 } 513 List<SourceAccountingLine> returnList = generateAccountSummary(items, includedItemTypeCodes, ITEM_TYPES_INCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 514 if (LOG.isDebugEnabled()) { 515 LOG.debug(methodName + " ended"); 516 } 517 return returnList; 518 } 519 520 /** 521 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryIncludeItemTypes(java.util.List, java.util.Set) 522 */ 523 @Override 524 public List<SourceAccountingLine> generateSummaryIncludeItemTypes(List<PurApItem> items, Set includedItemTypeCodes) { 525 String methodName = "generateSummaryIncludeItemTypes()"; 526 if (LOG.isDebugEnabled()) { 527 LOG.debug(methodName + " started"); 528 } 529 List<SourceAccountingLine> returnList = generateAccountSummary(items, includedItemTypeCodes, ITEM_TYPES_INCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 530 if (LOG.isDebugEnabled()) { 531 LOG.debug(methodName + " ended"); 532 } 533 return returnList; 534 } 535 536 /** 537 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryExcludeItemTypesAndNoZeroTotals(java.util.List, 538 * java.util.Set) 539 */ 540 @Override 541 public List<SourceAccountingLine> generateSummaryExcludeItemTypesAndNoZeroTotals(List<PurApItem> items, Set excludedItemTypeCodes) { 542 String methodName = "generateSummaryIncludeItemTypesAndNoZeroTotals()"; 543 if (LOG.isDebugEnabled()) { 544 LOG.debug(methodName + " started"); 545 } 546 List<SourceAccountingLine> returnList = generateAccountSummary(items, excludedItemTypeCodes, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 547 if (LOG.isDebugEnabled()) { 548 LOG.debug(methodName + " ended"); 549 } 550 return returnList; 551 } 552 553 /** 554 * Generates an account summary, that is it creates a list of source accounts by rounding up the purap accounts off of the purap 555 * items. 556 * 557 * @param items the items to determ 558 * @param itemTypeCodes the item types to determine whether to look at an item in combination with itemTypeCodesAreIncluded 559 * @param itemTypeCodesAreIncluded value to tell whether the itemTypeCodes parameter lists inclusion or exclusion variables 560 * @param useZeroTotals whether to include items with a zero dollar total 561 * @param useAlternateAmount an alternate amount used in certain cases for GL entry 562 * @return a list of source accounts 563 */ 564 protected List<SourceAccountingLine> generateAccountSummary(List<PurApItem> items, Set<String> itemTypeCodes, Boolean itemTypeCodesAreIncluded, Boolean useZeroTotals, Boolean useAlternateAmount, Boolean useTaxIncluded, Boolean taxableOnly) { 565 List<PurApItem> itemsToProcess = getProcessablePurapItems(items, itemTypeCodes, itemTypeCodesAreIncluded, useZeroTotals); 566 Map<PurApAccountingLine, KualiDecimal> accountMap = new HashMap<PurApAccountingLine, KualiDecimal>(); 567 568 for (PurApItem currentItem : itemsToProcess) { 569 if (PurApItemUtils.checkItemActive(currentItem)) { 570 List<PurApAccountingLine> sourceAccountingLines = currentItem.getSourceAccountingLines(); 571 572 // skip if item is not taxable and taxable only flag has been set 573 if (taxableOnly) { 574 PurchasingAccountsPayableDocument document = currentItem.getPurapDocument(); 575 if (!purapService.isTaxableForSummary(document.isUseTaxIndicator(), purapService.getDeliveryState(document), currentItem)) { 576 continue; 577 } 578 } 579 580 if (!useTaxIncluded) { 581 // if no use tax set the source accounting lines to a clone so we can update 582 // them to be based on the non tax amount 583 if(currentItem instanceof OleInvoiceItem) { 584 OleInvoiceItem invoiceItem = (OleInvoiceItem) currentItem; 585 if(invoiceItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && invoiceItem.getRelatedViews() != null) { 586 // invoiceItem.setRelatedViews(null); 587 PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(invoiceItem); 588 sourceAccountingLines = cloneItem.getSourceAccountingLines(); 589 updateAccountAmountsWithTotal(sourceAccountingLines, invoiceItem.getTotalRemitAmount()); 590 } 591 else { 592 PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(currentItem); 593 sourceAccountingLines = cloneItem.getSourceAccountingLines(); 594 updateAccountAmountsWithTotal(sourceAccountingLines, currentItem.getTotalRemitAmount()); 595 } 596 } 597 else { 598 PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(currentItem); 599 sourceAccountingLines = cloneItem.getSourceAccountingLines(); 600 updateAccountAmountsWithTotal(sourceAccountingLines, currentItem.getTotalRemitAmount()); 601 } 602 } 603 604 for (PurApAccountingLine account : sourceAccountingLines) { 605 606 // skip account if not taxable and taxable only flag is set 607 if (taxableOnly) { 608 PurchasingAccountsPayableDocument document = currentItem.getPurapDocument(); 609 // check if account is not taxable, if not skip this account 610 if (!purapService.isAccountingLineTaxable(account, purapService.isDeliveryStateTaxable(purapService.getDeliveryState(document)))) { 611 continue; 612 } 613 } 614 615 KualiDecimal total = KualiDecimal.ZERO; 616 if (account instanceof InvoiceAccount) { 617 if (((InvoiceAccount) account).getInvoiceItem() != null) { 618 if (((OleInvoiceItem)((InvoiceAccount) account).getInvoiceItem()).isDebitItem()) { 619 if (accountMap.containsKey(account)) { 620 total = accountMap.get(account); 621 } 622 623 if (useAlternateAmount) { 624 total = total.add(account.getAlternateAmountForGLEntryCreation()); 625 } else { 626 if (ObjectUtils.isNotNull(account.getAmount())) { 627 total = total.add(account.getAmount()); 628 } 629 } 630 } 631 else { 632 if (accountMap.containsKey(account)) { 633 total = accountMap.get(account); 634 } 635 636 if (useAlternateAmount) { 637 total = total.subtract(account.getAlternateAmountForGLEntryCreation()); 638 } else { 639 if (ObjectUtils.isNotNull(account.getAmount())) { 640 total = total.subtract(account.getAmount()); 641 } 642 } 643 } 644 } 645 646 } 647 else { 648 // getting the total to set on the account 649 if (accountMap.containsKey(account)) { 650 total = accountMap.get(account); 651 } 652 653 if (useAlternateAmount) { 654 total = total.add(account.getAlternateAmountForGLEntryCreation()); 655 } else { 656 if (ObjectUtils.isNotNull(account.getAmount())) { 657 total = total.add(account.getAmount()); 658 } 659 } 660 } 661 accountMap.put(account, total); 662 } 663 } 664 } 665 666 // convert list of PurApAccountingLine objects to SourceAccountingLineObjects 667 Iterator<PurApAccountingLine> iterator = accountMap.keySet().iterator(); 668 List<SourceAccountingLine> sourceAccounts = new ArrayList<SourceAccountingLine>(); 669 for (Iterator<PurApAccountingLine> iter = iterator; iter.hasNext(); ) { 670 PurApAccountingLine accountToConvert = iter.next(); 671 if (accountToConvert.isEmpty()) { 672 String errorMessage = "Found an 'empty' account in summary generation " + accountToConvert.toString(); 673 LOG.error("generateAccountSummary() " + errorMessage); 674 throw new RuntimeException(errorMessage); 675 } 676 KualiDecimal sourceLineTotal = accountMap.get(accountToConvert); 677 SourceAccountingLine sourceLine = accountToConvert.generateSourceAccountingLine(); 678 sourceLine.setAmount(sourceLineTotal); 679 sourceAccounts.add(sourceLine); 680 } 681 682 // sort the sourceAccounts list first by account number, then by object code, ignoring chart code 683 Collections.sort(sourceAccounts, new Comparator<SourceAccountingLine>() { 684 @Override 685 public int compare(SourceAccountingLine sal1, SourceAccountingLine sal2) { 686 int compare = 0; 687 if (sal1 != null && sal2 != null) { 688 if (sal1.getAccountNumber() != null && sal2.getAccountNumber() != null) { 689 compare = sal1.getAccountNumber().compareTo(sal2.getAccountNumber()); 690 if (compare == 0) { 691 if (sal1.getFinancialObjectCode() != null && sal2.getFinancialObjectCode() != null) { 692 compare = sal1.getFinancialObjectCode().compareTo(sal2.getFinancialObjectCode()); 693 } 694 } 695 } 696 } 697 return compare; 698 } 699 }); 700 701 return sourceAccounts; 702 } 703 704 /** 705 * This method takes a list of {@link PurchasingApItem} objects and parses through them to see if each one should be processed 706 * according the the other variables passed in.<br> 707 * <br> 708 * Example 1:<br> 709 * items = "ITEM", "SITM", "FRHT", "SPHD"<br> 710 * itemTypeCodes = "FRHT"<br> 711 * itemTypeCodesAreIncluded = ITEM_TYPES_EXCLUDED_VALUE<br> 712 * return items "ITEM", "SITM", "FRHT", "SPHD"<br> 713 * <br> 714 * <br> 715 * Example 2:<br> 716 * items = "ITEM", "SITM", "FRHT", "SPHD"<br> 717 * itemTypeCodes = "ITEM","FRHT"<br> 718 * itemTypeCodesAreIncluded = ITEM_TYPES_INCLUDED_VALUE<br> 719 * return items "ITEM", "FRHT"<br> 720 * 721 * @param items - list of {@link PurchasingApItem} objects that need to be parsed 722 * @param itemTypeCodes - list of {@link org.kuali.ole.module.purap.businessobject.ItemType} codes used in conjunction with 723 * itemTypeCodesAreIncluded parameter 724 * @param itemTypeCodesAreIncluded - value to tell whether the itemTypeCodes parameter lists inclusion or exclusion variables 725 * (see {@link #ITEM_TYPES_INCLUDED_VALUE}) 726 * @param useZeroTotals - value to tell whether to include zero dollar items (see {@link #ZERO_TOTALS_RETURNED_VALUE}) 727 * @return a list of {@link PurchasingApItem} objects that should be used for processing by calling method 728 */ 729 protected List<PurApItem> getProcessablePurapItems(List<PurApItem> items, Set itemTypeCodes, Boolean itemTypeCodesAreIncluded, Boolean useZeroTotals) { 730 String methodName = "getProcessablePurapItems()"; 731 List<PurApItem> newItemList = new ArrayList<PurApItem>(); 732 // error out if we have an invalid 'itemTypeCodesAreIncluded' value 733 if ((!(ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded))) && (!(ITEM_TYPES_EXCLUDED_VALUE.equals(itemTypeCodesAreIncluded)))) { 734 throwRuntimeException(methodName, "Invalid parameter found while trying to find processable items for dealing with purchasing/accounts payable accounts"); 735 } 736 for (PurApItem currentItem : items) { 737 if ((itemTypeCodes != null) && (!(itemTypeCodes.isEmpty()))) { 738 // we have at least one entry in our item type code list 739 boolean foundMatchInList = false; 740 // check to see if this item type code is in the list 741 for (Iterator iterator = itemTypeCodes.iterator(); iterator.hasNext(); ) { 742 String itemTypeCode = (String) iterator.next(); 743 // include this item if it's in the included list 744 if (itemTypeCode.equals(currentItem.getItemType().getItemTypeCode())) { 745 foundMatchInList = true; 746 break; 747 } 748 } 749 // check to see if item type code was found and if the list is describing included or excluded item types 750 if ((foundMatchInList) && (ITEM_TYPES_EXCLUDED_VALUE.equals(itemTypeCodesAreIncluded))) { 751 // this item type code is in the list 752 // this item type code is excluded so we skip it 753 continue; // skips current item 754 } else if ((!foundMatchInList) && (ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded))) { 755 // this item type code is not in the list 756 // this item type code is not included so we skip it 757 continue; // skips current item 758 } 759 } else { 760 // the item type code list is empty 761 if (ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded)) { 762 // the item type code list is empty and the list is supposed to contain the item types to include 763 throwRuntimeException(methodName, "Invalid parameter and list of items found while trying to find processable items for dealing with purchasing/accounts payable accounts"); 764 } 765 } 766 if ((ZERO_TOTALS_NOT_RETURNED_VALUE.equals(useZeroTotals)) && (ObjectUtils.isNull(currentItem.getExtendedPrice()) || ((KualiDecimal.ZERO.compareTo(currentItem.getExtendedPrice())) == 0))) { 767 // if we don't return zero dollar items then skip this one 768 continue; 769 } 770 newItemList.add(currentItem); 771 } 772 return newItemList; 773 } 774 775 /** 776 * @see org.kuali.ole.module.purap.service.PurapAccountingService#updateAccountAmounts(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument) 777 */ 778 @Override 779 public void updateAccountAmounts(PurchasingAccountsPayableDocument document) { 780 781 PurchasingAccountsPayableDocumentBase purApDocument = (PurchasingAccountsPayableDocumentBase) document; 782 String accountDistributionMethod = purApDocument.getAccountDistributionMethod(); 783 784 KualiRuleService kualiRuleService = SpringContext.getBean(KualiRuleService.class); 785 // olePaymentRequestDocument = (OlePaymentRequestDocument)document; 786 // the percent at fiscal approve 787 // don't update if past the AP review level 788 if ((document instanceof PaymentRequestDocument) && purapService.isFullDocumentEntryCompleted(document)) { 789 // update the percent but don't update the amounts if preq and past full entry 790 convertMoneyToPercent((PaymentRequestDocument) document); 791 return; 792 } 793 if ((document instanceof OleInvoiceDocument) && purapService.isFullDocumentEntryCompleted(document)) { 794 // update the percent but don't update the amounts if preq and past full entry 795 convertMoneyToPercent((OleInvoiceDocument) document); 796 return; 797 } 798 document.fixItemReferences(); 799 800 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 801 //if distribution method is sequential and document is PREQ or VCM then... 802// if (((document instanceof PaymentRequestDocument) 803// || (document instanceof VendorCreditMemoDocument)) 804// && PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) { 805// if (document instanceof VendorCreditMemoDocument) { 806// VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) document; 807// cmDocument.updateExtendedPriceOnItems(); 808// } 809// 810// // update the accounts amounts for PREQ and distribution method = sequential 811// for (PurApItem item : document.getItems()) { 812// updatePreqItemAccountAmounts(item); 813// } 814// 815// return; 816// } 817 818 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 819 //if distribution method is proportional and document is PREQ or VCM then... 820// if (((document instanceof PaymentRequestDocument) || (document instanceof VendorCreditMemoDocument)) && PurapConstants.AccountDistributionMethodCodes.PROPORTIONAL_CODE.equalsIgnoreCase(accountDistributionMethod)) { 821// // update the accounts amounts for PREQ and distribution method = sequential 822// if (document instanceof VendorCreditMemoDocument) { 823// VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) document; 824// cmDocument.updateExtendedPriceOnItems(); 825// } 826 for(PurApItem purApItem:document.getItems()){ 827 for(PurApAccountingLine oldSourceAccountingLine:purApItem.getSourceAccountingLines()) { 828 if(oldSourceAccountingLine instanceof OleRequisitionAccount) { 829 ((OleRequisitionAccount)oldSourceAccountingLine).setExistingAmount(oldSourceAccountingLine.getAmount()); 830 } 831 else if(oldSourceAccountingLine instanceof OlePurchaseOrderAccount) { 832 ((OlePurchaseOrderAccount)oldSourceAccountingLine).setExistingAmount(oldSourceAccountingLine.getAmount()); 833 } 834 else if(oldSourceAccountingLine instanceof PaymentRequestAccount) { 835 ((PaymentRequestAccount)oldSourceAccountingLine).setExistingAmount(oldSourceAccountingLine.getAmount()); 836 } 837 else if(oldSourceAccountingLine instanceof InvoiceAccount) { 838 ((InvoiceAccount)oldSourceAccountingLine).setExistingAmount(oldSourceAccountingLine.getAmount()); 839 } 840 } 841 } 842 for (PurApItem item : document.getItems()) { 843 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 844// boolean rulePassed = true; 845// // check any business rules 846// rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item)); 847// 848// if (rulePassed) { 849// updatePreqProportionalItemAccountAmounts(item); 850 updateItemAccountAmounts(item); 851 if (item instanceof OlePaymentRequestItem) { 852 updateItemAccountAmountsForAdditionalCharge(item, (OlePaymentRequestDocument) document); 853 } else if (item instanceof OleCreditMemoItem) { 854 updateItemAccountAmountsForAdditionalCharge(item, (OleVendorCreditMemoDocument) document); 855 } else if (item instanceof OleInvoiceItem) { 856 updateItemAccountAmountsForAdditionalCharge(item, (OleInvoiceDocument) document); 857 } else { 858 updateItemAccountAmounts(item); 859// updatePreqProportionalItemAccountAmounts(item); 860 } 861// } 862 } 863 864 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 865// return; 866// } 867 868 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 869 //No recalculate if the account distribution method code is equal to "S" sequential ON REQ or POs.. 870// if (PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) { 871// for (PurApItem item : document.getItems()) { 872// boolean rulePassed = true; 873// // check any business rules 874// rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item)); 875// 876// return; 877// } 878// } 879// 880// //do recalculate only if the account distribution method code is not equal to "S" sequential method. 881// if (!PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) { 882// for (PurApItem item : document.getItems()) { 883// boolean rulePassed = true; 884// // check any business rules 885// rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item)); 886// 887// if (rulePassed) { 888// updateItemAccountAmounts(item); 889// } 890// } 891// } 892 } 893 894 public void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OlePaymentRequestDocument document) { 895 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 896 OlePaymentRequestItem olePaymentRequestItem = (OlePaymentRequestItem) item; 897 KualiDecimal prorateSurcharge = new KualiDecimal(); 898 KualiDecimal qty = KualiDecimal.ZERO; 899 if (olePaymentRequestItem.getItemSurcharge() == null) { 900 prorateSurcharge = KualiDecimal.ZERO; 901 } else { 902 qty = olePaymentRequestItem.getItemQuantity(); 903 prorateSurcharge = new KualiDecimal(olePaymentRequestItem.getItemSurcharge()); 904 } 905 KualiDecimal totalAmount = item.getTotalAmount(); 906 totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty)); 907 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 908 } 909 910 public void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OleInvoiceDocument document) { 911 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 912 OleInvoiceItem oleInvoiceItem = (OleInvoiceItem) item; 913 KualiDecimal prorateSurcharge = new KualiDecimal(); 914 KualiDecimal qty = KualiDecimal.ZERO; 915 if (oleInvoiceItem.getItemSurcharge() == null) { 916 prorateSurcharge = KualiDecimal.ZERO; 917 } else { 918 qty = oleInvoiceItem.getItemQuantity(); 919 prorateSurcharge = new KualiDecimal(oleInvoiceItem.getItemSurcharge()); 920 } 921 KualiDecimal totalAmount = item.getTotalAmount(); 922 totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty)); 923 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 924 } 925 926 private void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OleVendorCreditMemoDocument document) { 927 928 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 929 OleCreditMemoItem oleCreditMemoItem = (OleCreditMemoItem) item; 930 KualiDecimal prorateSurcharge = new KualiDecimal(); 931 KualiDecimal qty = KualiDecimal.ZERO; 932 if (oleCreditMemoItem.getItemSurcharge() == null) { 933 prorateSurcharge = KualiDecimal.ZERO; 934 } else { 935 qty = oleCreditMemoItem.getItemQuantity(); 936 prorateSurcharge = new KualiDecimal(oleCreditMemoItem.getItemSurcharge()); 937 } 938 KualiDecimal totalAmount = item.getTotalAmount(); 939 totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty)); 940 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 941 942 } 943 944 /** 945 * @see org.kuali.ole.module.purap.service.PurapAccountingService#updateItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem) 946 */ 947 @Override 948 public void updateItemAccountAmounts(PurApItem item) { 949 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 950 KualiDecimal totalAmount = item.getTotalAmount(); 951 if (item.getItemType().isAdditionalChargeIndicator()) { 952 updateAccountAmountsWithTotalForAdditionalCharge(sourceAccountingLines, totalAmount); 953 } else { 954 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 955 } 956 } 957 958 /*public void updateItemAccountAmountsForAdditionalCharge(PurApItem item) { 959 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 960 KualiDecimal totalAmount = item.getTotalAmount(); 961 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 962 }*/ 963 964 /** 965 * calculates values for a list of accounting lines based on an amount 966 * 967 * @param sourceAccountingLines 968 * @param totalAmount 969 */ 970 971 public <T extends PurApAccountingLine> void updateAccountAmountsWithTotalForAdditionalCharge(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 972 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 973 974 KualiDecimal accountTotal = KualiDecimal.ZERO; 975 T lastAccount = null; 976 977 978 for (T account : sourceAccountingLines) { 979 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 980 BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString()).divide(new BigDecimal(100)); 981 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString())).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 982 } else { 983 account.setAmount(KualiDecimal.ZERO); 984 } 985 accountTotal = accountTotal.add(account.getAmount()); 986 lastAccount = account; 987 } 988 989 // put excess on last account 990 if (lastAccount != null) { 991 KualiDecimal difference = totalAmount.subtract(accountTotal); 992 lastAccount.setAmount(lastAccount.getAmount().add(difference)); 993 } 994 } else { 995 // zero out if extended price is zero 996 for (T account : sourceAccountingLines) { 997 account.setAmount(KualiDecimal.ZERO); 998 } 999 } 1000 } 1001 1002 public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal2(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 1003 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 1004 1005 KualiDecimal accountTotal = KualiDecimal.ZERO; 1006 T lastAccount = null; 1007 1008 1009 for (T account : sourceAccountingLines) { 1010 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1011 // OlePaymentRequestItem item = new OlePaymentRequestItem(); 1012 BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString()).divide(new BigDecimal(100)); 1013 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString())).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 1014 } else { 1015 account.setAmount(KualiDecimal.ZERO); 1016 } 1017 accountTotal = accountTotal.add(account.getAmount()); 1018 lastAccount = account; 1019 } 1020 1021 // put excess on last account 1022 if (lastAccount != null) { 1023 KualiDecimal difference = totalAmount.subtract(accountTotal); 1024 lastAccount.setAmount(lastAccount.getAmount().add(difference)); 1025 } 1026 } else { 1027 // zero out if extended price is zero 1028 for (T account : sourceAccountingLines) { 1029 account.setAmount(KualiDecimal.ZERO); 1030 } 1031 } 1032 } 1033 1034 /** 1035 * calculates values for a list of accounting lines based on an amount 1036 * 1037 * @param sourceAccountingLines 1038 * @param totalAmount 1039 */ 1040 @Override 1041 public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 1042 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount, new KualiDecimal(0)); 1043 } 1044 1045 /** 1046 * calculates values for a list of accounting lines based on an amount taking discount into account 1047 * 1048 * @param sourceAccountingLines 1049 * @param totalAmount 1050 * @param discountAmount 1051 */ 1052 @Override 1053 public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal(List<T> sourceAccountingLines, 1054 KualiDecimal totalAmount, KualiDecimal discountAmount) { 1055 1056 // if we have a discount, then we need to base the amounts on the discount, but the percent on the total 1057 boolean noDiscount = true; 1058 if ((discountAmount != null) && KualiDecimal.ZERO.compareTo(discountAmount) != 0) { 1059 noDiscount = false; 1060 } 1061 1062 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 1063 1064 KualiDecimal accountTotal = KualiDecimal.ZERO; 1065 BigDecimal accountTotalPercent = BigDecimal.ZERO; 1066 T lastAccount = null; 1067 1068 for (T account : sourceAccountingLines) { 1069 if (ObjectUtils.isNotNull(account.getAccountLinePercent()) || ObjectUtils.isNotNull(account.getAmount())) { 1070 if (ObjectUtils.isNotNull(account.getAmount()) && account.getAmount().isGreaterThan(KualiDecimal.ZERO)) { 1071 KualiDecimal amt = account.getAmount(); 1072 KualiDecimal calculatedPercent = new KualiDecimal(amt.multiply(new KualiDecimal(100)).divide(totalAmount).toString()); 1073 account.setAccountLinePercent(calculatedPercent.bigDecimalValue().setScale(BIG_DECIMAL_SCALE)); 1074 } 1075 1076 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1077 BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString()).divide(new BigDecimal(100)); 1078 if (noDiscount) { 1079 if (ObjectUtils.isNull(account.getAmount()) || account.getAmount().isZero()) { 1080 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString())) 1081 .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 1082 } 1083 } else { 1084 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(discountAmount.toString())) 1085 .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 1086 } 1087 } 1088 } 1089 1090 if (ObjectUtils.isNotNull(account.getAmount())) { 1091 accountTotal = accountTotal.add(account.getAmount()); 1092 } 1093 1094 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1095 accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent()); 1096 } 1097 1098 lastAccount = account; 1099 } 1100 1101 // put excess on last account 1102 if (lastAccount != null) { 1103 KualiDecimal difference = new KualiDecimal(0); 1104 if (noDiscount) { 1105 difference = totalAmount.subtract(accountTotal); 1106 } else { 1107 difference = discountAmount.subtract(accountTotal); 1108 } 1109 if (ObjectUtils.isNotNull(lastAccount.getAmount())) { 1110 lastAccount.setAmount(lastAccount.getAmount()); 1111 } 1112 1113 BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent).setScale(BIG_DECIMAL_SCALE); 1114 if (ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) { 1115 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent()); 1116 } 1117 } 1118 } else { 1119 // zero out if extended price is zero 1120 for (T account : sourceAccountingLines) { 1121 if (ObjectUtils.isNotNull(account.getAmount())) { 1122 account.setAmount(KualiDecimal.ZERO); 1123 } 1124 } 1125 } 1126 } 1127 1128 /** 1129 * @see org.kuali.ole.module.purap.service.PurapAccountingService#updatePreqProportionalItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem) 1130 */ 1131 @Override 1132 public void updatePreqProportionalItemAccountAmounts(PurApItem item) { 1133 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 1134 KualiDecimal totalAmount = item.getTotalAmount(); 1135 1136 updatePreqProporationalAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 1137 } 1138 1139 /** 1140 * calculates values for a list of accounting lines based on an amount for proportional method 1141 * 1142 * @param sourceAccountingLines 1143 * @param totalAmount 1144 */ 1145 @Override 1146 public <T extends PurApAccountingLine> void updatePreqProporationalAccountAmountsWithTotal(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 1147 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 1148 KualiDecimal accountTotal = KualiDecimal.ZERO; 1149 BigDecimal accountTotalPercent = BigDecimal.ZERO; 1150 T lastAccount = null; 1151 1152 for (T account : sourceAccountingLines) { 1153 if (ObjectUtils.isNotNull(account.getAccountLinePercent()) || ObjectUtils.isNotNull(account.getAmount())) { 1154 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1155 BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString()).divide(new BigDecimal(100)); 1156 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString())).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 1157 } 1158 } 1159 1160 if (ObjectUtils.isNotNull(account.getAmount())) { 1161 accountTotal = accountTotal.add(account.getAmount()); 1162 } 1163 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1164 accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent()); 1165 } 1166 1167 lastAccount = account; 1168 } 1169 1170 // put excess on last account 1171 if (lastAccount != null) { 1172 KualiDecimal difference = totalAmount.subtract(accountTotal); 1173 if (ObjectUtils.isNotNull(lastAccount.getAmount())) { 1174 lastAccount.setAmount(lastAccount.getAmount().add(difference)); 1175 } 1176 1177 BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent).setScale(BIG_DECIMAL_SCALE); 1178 if (ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) { 1179 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference)); 1180 } 1181 } 1182 } 1183 } 1184 1185 /** 1186 * @see org.kuali.ole.module.purap.service.PurapAccountingService#updatePreqItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem) 1187 */ 1188 @Override 1189 public void updatePreqItemAccountAmounts(PurApItem item) { 1190 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 1191 KualiDecimal totalAmount = item.getTotalAmount(); 1192 1193 updatePreqAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 1194 } 1195 1196 /** 1197 * calculates values for a list of accounting lines based on an amount. Preq item's extended 1198 * cost is distributed to the accounting lines. 1199 * 1200 * @param sourceAccountingLines 1201 * @param totalAmount 1202 */ 1203 @Override 1204 public <T extends PurApAccountingLine> void updatePreqAccountAmountsWithTotal(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 1205 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 1206 KualiDecimal accountTotal = KualiDecimal.ZERO; 1207 BigDecimal accountTotalPercent = BigDecimal.ZERO; 1208 T lastAccount = null; 1209 1210 for (T account : sourceAccountingLines) { 1211 //look at lines where amount is non-zero.. 1212 if (account.getAmount().isGreaterThan(KualiDecimal.ZERO)) { 1213 if (totalAmount.isZero()) { 1214 account.setAmount(KualiDecimal.ZERO); 1215 } else { 1216 if (account.getAmount().isGreaterThan(totalAmount)) { 1217 account.setAmount(totalAmount); 1218 } 1219 } 1220 } 1221 1222 totalAmount = totalAmount.subtract(account.getAmount()); 1223 } 1224 1225 if (totalAmount.isGreaterThan(KualiDecimal.ZERO)) { 1226 for (T account : sourceAccountingLines) { 1227 if (account.getAmount().isZero() || account.getAccountLinePercent().compareTo(BigDecimal.ZERO) == 1) { 1228 KualiDecimal priorAmount = account.getAmount(); 1229 account.setAmount(account.getAmount().add(new KualiDecimal(account.getAccountLinePercent()).multiply(totalAmount).divide(new KualiDecimal(100)))); 1230 accountTotal = accountTotal.add(account.getAmount().subtract(priorAmount)); 1231 lastAccount = account; 1232 } 1233 } 1234 } 1235 1236 accountTotal = totalAmount.subtract(accountTotal); 1237 1238 if (accountTotal.isGreaterThan(KualiDecimal.ZERO) && ObjectUtils.isNotNull(lastAccount)) { 1239 //add the difference to the last overage account.... 1240 lastAccount.setAmount(lastAccount.getAmount().add(accountTotal)); 1241 } 1242 } 1243 } 1244 1245 public List<PurApAccountingLine> generatePercentSummary(PurchasingAccountsPayableDocument purapDoc) { 1246 List<PurApAccountingLine> accounts = new ArrayList<PurApAccountingLine>(); 1247 for (PurApItem currentItem : purapDoc.getItems()) { 1248 if (PurApItemUtils.checkItemActive(currentItem)) { 1249 for (PurApAccountingLine account : currentItem.getSourceAccountingLines()) { 1250 boolean thisAccountAlreadyInSet = false; 1251 for (Object element : accounts) { 1252 PurApAccountingLine alreadyAddedAccount = (PurApAccountingLine) element; 1253 1254 1255 if (alreadyAddedAccount.accountStringsAreEqual(account)) { 1256 BigDecimal alreadyAddedAccountLinePercent = BigDecimal.ZERO; 1257 if (ObjectUtils.isNotNull(alreadyAddedAccount.getAccountLinePercent())) { 1258 alreadyAddedAccountLinePercent = alreadyAddedAccount.getAccountLinePercent(); 1259 } 1260 BigDecimal accountLinePercent = BigDecimal.ZERO; 1261 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1262 accountLinePercent = account.getAccountLinePercent(); 1263 } 1264 1265 alreadyAddedAccount.setAccountLinePercent(alreadyAddedAccountLinePercent.add(accountLinePercent)); 1266 1267 thisAccountAlreadyInSet = true; 1268 break; 1269 } 1270 } 1271 if (!thisAccountAlreadyInSet) { 1272 PurApAccountingLine accountToAdd = (PurApAccountingLine) ObjectUtils.deepCopy(account); 1273 accounts.add(accountToAdd); 1274 } 1275 } 1276 } 1277 } 1278 return accounts; 1279 } 1280 1281 /** 1282 * @see org.kuali.ole.module.purap.service.PurapAccountingService#convertMoneyToPercent(org.kuali.ole.module.purap.document.PaymentRequestDocument) 1283 */ 1284 @Override 1285 public void convertMoneyToPercent(PaymentRequestDocument pr) { 1286 LOG.debug("convertMoneyToPercent() started"); 1287 1288 int itemNbr = 0; 1289 1290 for (Iterator<PaymentRequestItem> iter = pr.getItems().iterator(); iter.hasNext(); ) { 1291 PaymentRequestItem item = iter.next(); 1292 1293 itemNbr++; 1294 String identifier = item.getItemIdentifierString(); 1295 1296 if (item.getTotalAmount() != null && item.getTotalAmount().isNonZero()) { 1297 int numOfAccounts = item.getSourceAccountingLines().size(); 1298 BigDecimal percentTotal = BigDecimal.ZERO; 1299 KualiDecimal accountTotal = KualiDecimal.ZERO; 1300 int accountIdentifier = 0; 1301 1302 KualiDecimal addChargeItem = KualiDecimal.ZERO; 1303 KualiDecimal lineItemPreTaxTotal = KualiDecimal.ZERO; 1304 KualiDecimal prorateSurcharge = KualiDecimal.ZERO; 1305 if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice() != null && item.getExtendedPrice().compareTo(KualiDecimal.ZERO) != 0) { 1306 if (((OlePaymentRequestItem) item).getItemSurcharge() != null) { 1307 prorateSurcharge = new KualiDecimal(((OlePaymentRequestItem) item).getItemSurcharge()).multiply(item.getItemQuantity()); 1308 } 1309 } 1310 1311 for (PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { 1312 accountIdentifier++; 1313 PaymentRequestAccount account = (PaymentRequestAccount) purApAccountingLine; 1314 1315 // account.getAmount returns the wrong value for trade in source accounting lines... 1316 KualiDecimal accountAmount = KualiDecimal.ZERO; 1317 if (ObjectUtils.isNotNull(account.getAmount())) { 1318 accountAmount = account.getAmount(); 1319 } 1320 1321 BigDecimal tmpPercent = BigDecimal.ZERO; 1322 KualiDecimal extendedPrice = item.getTotalAmount(); 1323 tmpPercent = accountAmount.bigDecimalValue().divide(extendedPrice.bigDecimalValue(), PurapConstants.CREDITMEMO_PRORATION_SCALE.intValue(), KualiDecimal.ROUND_BEHAVIOR); 1324 1325 if (accountIdentifier == numOfAccounts) { 1326 // if on last account, calculate the percent by subtracting current percent total from 1 1327 tmpPercent = BigDecimal.ONE.subtract(percentTotal); 1328 } 1329 1330 // test that the above amount is correct, if so just check that the total of all these matches the item total 1331 BigDecimal calcAmountBd = tmpPercent.multiply(extendedPrice.bigDecimalValue()); 1332 calcAmountBd = calcAmountBd.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR); 1333 KualiDecimal calcAmount = new KualiDecimal(calcAmountBd); 1334 calcAmount = calcAmount.subtract(prorateSurcharge); 1335 if (calcAmount.compareTo(accountAmount) != 0) { 1336 // rounding error 1337 if (LOG.isDebugEnabled()) { 1338 LOG.debug("convertMoneyToPercent() Rounding error on " + account); 1339 } 1340 String param1 = identifier + "." + accountIdentifier; 1341 String param2 = calcAmount.bigDecimalValue().subtract(accountAmount.bigDecimalValue()).toString(); 1342 GlobalVariables.getMessageMap().putError(item.getItemIdentifierString(), PurapKeyConstants.ERROR_ITEM_ACCOUNTING_ROUNDING, param1, param2); 1343 account.setAmount(calcAmount); 1344 } 1345 1346 // update percent 1347 if (LOG.isDebugEnabled()) { 1348 LOG.debug("convertMoneyToPercent() updating percent to " + tmpPercent); 1349 } 1350 account.setAccountLinePercent(tmpPercent.multiply(new BigDecimal(100))); 1351 1352 // check total based on adjusted amount 1353 accountTotal = accountTotal.add(calcAmount); 1354 percentTotal = percentTotal.add(tmpPercent); 1355 } 1356 } 1357 } 1358 } 1359 1360 1361 @Override 1362 public void convertMoneyToPercent(InvoiceDocument inv) { 1363 LOG.debug("convertMoneyToPercent() started"); 1364 1365 int itemNbr = 0; 1366 1367 for (Iterator<InvoiceItem> iter = inv.getItems().iterator(); iter.hasNext(); ) { 1368 InvoiceItem item = iter.next(); 1369 1370 itemNbr++; 1371 String identifier = item.getItemIdentifierString(); 1372 1373 if (item.getTotalAmount() != null && item.getTotalAmount().isNonZero()) { 1374 int numOfAccounts = item.getSourceAccountingLines().size(); 1375 BigDecimal percentTotal = BigDecimal.ZERO; 1376 KualiDecimal accountTotal = KualiDecimal.ZERO; 1377 int accountIdentifier = 0; 1378 1379 KualiDecimal addChargeItem = KualiDecimal.ZERO; 1380 KualiDecimal lineItemPreTaxTotal = KualiDecimal.ZERO; 1381 KualiDecimal prorateSurcharge = KualiDecimal.ZERO; 1382 if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice() != null && item.getExtendedPrice().compareTo(KualiDecimal.ZERO) != 0) { 1383 if (((OleInvoiceItem) item).getItemSurcharge() != null) { 1384 prorateSurcharge = new KualiDecimal(((OleInvoiceItem) item).getItemSurcharge()).multiply(item.getItemQuantity()); 1385 } 1386 } 1387 1388 for (PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { 1389 accountIdentifier++; 1390 InvoiceAccount account = (InvoiceAccount) purApAccountingLine; 1391 1392 // account.getAmount returns the wrong value for trade in source accounting lines... 1393 KualiDecimal accountAmount = KualiDecimal.ZERO; 1394 if (ObjectUtils.isNotNull(account.getAmount())) { 1395 accountAmount = account.getAmount(); 1396 } 1397 1398 BigDecimal tmpPercent = BigDecimal.ZERO; 1399 KualiDecimal extendedPrice = item.getTotalAmount(); 1400 tmpPercent = accountAmount.bigDecimalValue().divide(extendedPrice.bigDecimalValue(), PurapConstants.CREDITMEMO_PRORATION_SCALE.intValue(), KualiDecimal.ROUND_BEHAVIOR); 1401 1402 if (accountIdentifier == numOfAccounts) { 1403 // if on last account, calculate the percent by subtracting current percent total from 1 1404 tmpPercent = BigDecimal.ONE.subtract(percentTotal); 1405 } 1406 1407 // test that the above amount is correct, if so just check that the total of all these matches the item total 1408 BigDecimal calcAmountBd = tmpPercent.multiply(extendedPrice.bigDecimalValue()); 1409 calcAmountBd = calcAmountBd.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR); 1410 KualiDecimal calcAmount = new KualiDecimal(calcAmountBd); 1411 calcAmount = calcAmount.subtract(prorateSurcharge); 1412 if (calcAmount.compareTo(accountAmount) != 0) { 1413 // rounding error 1414 if (LOG.isDebugEnabled()) { 1415 LOG.debug("convertMoneyToPercent() Rounding error on " + account); 1416 } 1417 String param1 = identifier + "." + accountIdentifier; 1418 String param2 = calcAmount.bigDecimalValue().subtract(accountAmount.bigDecimalValue()).toString(); 1419 GlobalVariables.getMessageMap().putError(item.getItemIdentifierString(), PurapKeyConstants.ERROR_ITEM_ACCOUNTING_ROUNDING, param1, param2); 1420 account.setAmount(calcAmount); 1421 } 1422 1423 // update percent 1424 if (LOG.isDebugEnabled()) { 1425 LOG.debug("convertMoneyToPercent() updating percent to " + tmpPercent); 1426 } 1427 account.setAccountLinePercent(tmpPercent.multiply(new BigDecimal(100))); 1428 1429 // check total based on adjusted amount 1430 accountTotal = accountTotal.add(calcAmount); 1431 percentTotal = percentTotal.add(tmpPercent); 1432 } 1433 } 1434 } 1435 } 1436 1437 /** 1438 * @see org.kuali.ole.module.purap.service.PurapAccountingService#deleteSummaryAccounts(java.lang.Integer, java.lang.String) 1439 */ 1440 @Override 1441 public void deleteSummaryAccounts(Integer purapDocumentIdentifier, String docType) { 1442 if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) { 1443 purApAccountingDao.deleteSummaryAccountsbyPaymentRequestIdentifier(purapDocumentIdentifier); 1444 } else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) { 1445 purApAccountingDao.deleteSummaryAccountsbyCreditMemoIdentifier(purapDocumentIdentifier); 1446 } else if (PurapDocTypeCodes.INVOICE_DOCUMENT.equals(docType)) { 1447 purApAccountingDao.deleteSummaryAccountsbyInvoiceIdentifier(purapDocumentIdentifier); 1448 } 1449 } 1450 1451 @Override 1452 public List getAccountsPayableSummaryAccounts(Integer purapDocumentIdentifier, String docType) { 1453 if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) { 1454 return getSummaryAccountsbyPaymentRequestIdentifier(purapDocumentIdentifier); 1455 } else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) { 1456 getSummaryAccountsbyCreditMemoIdentifier(purapDocumentIdentifier); 1457 } else if (PurapDocTypeCodes.INVOICE_DOCUMENT.equals(docType)) { 1458 getSummaryAccountsbyInvoiceIdentifier(purapDocumentIdentifier); 1459 } 1460 return null; 1461 } 1462 1463 @Override 1464 public List<PurApAccountingLine> getAccountsFromItem(PurApItem item) { 1465 return purApAccountingDao.getAccountingLinesForItem(item); 1466 } 1467 1468 @Override 1469 public List<SourceAccountingLine> generateSourceAccountsForVendorRemit(PurchasingAccountsPayableDocument document) { 1470 // correct initial amounts or percents 1471 updateAccountAmounts(document); 1472 List<SourceAccountingLine> vendorSummaryAccounts = new ArrayList<SourceAccountingLine>(); 1473 1474 // update accounts here with amounts to send to vendor 1475 vendorSummaryAccounts = generateSummaryWithNoZeroTotalsNoUseTax(document.getItems()); 1476 1477 return vendorSummaryAccounts; 1478 } 1479 1480 /** 1481 * gets sum total of accounts 1482 * 1483 * @param accounts 1484 * @return 1485 */ 1486 1487 protected KualiDecimal calculateSumTotal(List<SourceAccountingLine> accounts) { 1488 KualiDecimal total = KualiDecimal.ZERO; 1489 for (SourceAccountingLine accountingLine : accounts) { 1490 KualiDecimal amt = KualiDecimal.ZERO; 1491 if (ObjectUtils.isNotNull(accountingLine.getAmount())) { 1492 amt = accountingLine.getAmount(); 1493 } 1494 total = total.add(amt); 1495 } 1496 return total; 1497 } 1498 1499 /** 1500 * Replaces amount field with prorated tax amount in list 1501 * 1502 * @param accounts list of accounts 1503 * @param useTax tax to be allocated to these accounts 1504 * @param newSourceLines rewrites the source account lines 1505 */ 1506 1507 protected void convertAmtToTax(List<PurApAccountingLine> accounts, KualiDecimal useTax, List<SourceAccountingLine> newSourceLines) { 1508 final BigDecimal HUNDRED = new BigDecimal(100); 1509 PurApAccountingLine purApAccountingLine; 1510 BigDecimal proratedAmtBD; 1511 KualiDecimal proratedAmt; 1512 // convert back to source 1513 KualiDecimal total = KualiDecimal.ZERO; 1514 int last = accounts.size() - 1; 1515 for (int i = 0; i < last; i++) { 1516 purApAccountingLine = accounts.get(i); 1517 BigDecimal linePercent = BigDecimal.ZERO; 1518 if (ObjectUtils.isNotNull(purApAccountingLine.getAccountLinePercent())) { 1519 linePercent = purApAccountingLine.getAccountLinePercent(); 1520 } 1521 1522 proratedAmtBD = useTax.bigDecimalValue().multiply(linePercent); 1523 // last object takes the rest of the amount 1524 // proratedAmt = (accounts.indexOf(purApAccountingLine) == last) ? useTax.subtract(total) : proratedAmt.divide(HUNDRED); 1525 proratedAmtBD = proratedAmtBD.divide(HUNDRED); 1526 proratedAmt = new KualiDecimal(proratedAmtBD); 1527 SourceAccountingLine acctLine = purApAccountingLine.generateSourceAccountingLine(); 1528 acctLine.setAmount(proratedAmt); 1529 newSourceLines.add(acctLine); 1530 total = total.add(proratedAmt); 1531 } 1532 // update last object with remaining balance 1533 proratedAmt = useTax.subtract(total); 1534 purApAccountingLine = accounts.get(last); 1535 SourceAccountingLine acctLine = purApAccountingLine.generateSourceAccountingLine(); 1536 acctLine.setAmount(proratedAmt); 1537 newSourceLines.add(acctLine); 1538 } 1539 1540 /** 1541 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateUseTaxAccount(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument) 1542 */ 1543 @Override 1544 public List<UseTaxContainer> generateUseTaxAccount(PurchasingAccountsPayableDocument document) { 1545 List<UseTaxContainer> useTaxAccounts = new ArrayList<UseTaxContainer>(); 1546 1547 HashMap<PurApItemUseTax, UseTaxContainer> useTaxItemMap = new HashMap<PurApItemUseTax, UseTaxContainer>(); 1548 Class accountingLineClass = null; 1549 if (!document.isUseTaxIndicator()) { 1550 // not useTax, return 1551 return useTaxAccounts; 1552 } 1553 for (PurApItem purApItem : document.getItems()) { 1554 if (!purApItem.getUseTaxItems().isEmpty()) { 1555 if (accountingLineClass == null) { 1556 accountingLineClass = purApItem.getAccountingLineClass(); 1557 } 1558 UseTaxContainer useTaxContainer = new UseTaxContainer(); 1559 for (PurApItemUseTax itemUseTax : purApItem.getUseTaxItems()) { 1560 if (useTaxItemMap.containsKey(itemUseTax)) { 1561 useTaxContainer = useTaxItemMap.get(itemUseTax); 1562 PurApItemUseTax exisitingItemUseTax = useTaxContainer.getUseTax(); 1563 // if already in set we need to add on the old amount 1564 KualiDecimal tax = exisitingItemUseTax.getTaxAmount(); 1565 tax = tax.add(itemUseTax.getTaxAmount()); 1566 exisitingItemUseTax.setTaxAmount(tax); 1567 1568 List<PurApItem> items = useTaxContainer.getItems(); 1569 items.add(purApItem); 1570 useTaxContainer.setItems(items); 1571 1572 } else { 1573 useTaxContainer = new UseTaxContainer(itemUseTax, purApItem); 1574 useTaxItemMap.put(itemUseTax, useTaxContainer); 1575 useTaxAccounts.add(useTaxContainer); 1576 } 1577 } 1578 } 1579 } 1580 // iterate over useTaxAccounts and set summary accounts using proration 1581 for (UseTaxContainer useTaxContainer : useTaxAccounts) { 1582 1583 // create summary from items 1584 List<SourceAccountingLine> origSourceAccounts = this.generateSummaryWithNoZeroTotals(useTaxContainer.getItems()); 1585 KualiDecimal totalAmount = calculateSumTotal(origSourceAccounts); 1586 List<PurApAccountingLine> accountingLines = generateAccountDistributionForProration(origSourceAccounts, totalAmount, PurapConstants.PRORATION_SCALE, accountingLineClass); 1587 1588 1589 List<SourceAccountingLine> newSourceLines = new ArrayList<SourceAccountingLine>(); 1590 // convert back to source 1591 convertAmtToTax(accountingLines, useTaxContainer.getUseTax().getTaxAmount(), newSourceLines); 1592 1593 // do we need an update accounts here? 1594 useTaxContainer.setAccounts(newSourceLines); 1595 } 1596 1597 useTaxAccounts = new ArrayList<UseTaxContainer>(useTaxItemMap.values()); 1598 return useTaxAccounts; 1599 } 1600 1601 /** 1602 * @see org.kuali.ole.module.purap.service.PurapAccountingService#isTaxAccount(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument, 1603 * org.kuali.ole.sys.businessobject.SourceAccountingLine) 1604 */ 1605 @Override 1606 public boolean isTaxAccount(PurchasingAccountsPayableDocument document, SourceAccountingLine account) { 1607 boolean isTaxAccount = false; 1608 1609 // check if the summary account is for tax withholding 1610 if (document instanceof PaymentRequestDocument) { 1611 String incomeClassCode = ((PaymentRequestDocument) document).getTaxClassificationCode(); 1612 if (StringUtils.isNotEmpty(incomeClassCode)) { 1613 1614 String federalChartCode = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX); 1615 String federalAccountNumber = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX); 1616 String federalObjectCode = parameterService.getSubParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX, incomeClassCode); 1617 1618 String stateChartCode = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX); 1619 String stateAccountNumber = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX); 1620 String stateObjectCode = parameterService.getSubParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX, incomeClassCode); 1621 1622 String chartCode = account.getChartOfAccountsCode(); 1623 String accountNumber = account.getAccountNumber(); 1624 String objectCode = account.getFinancialObjectCode(); 1625 1626 boolean isFederalAccount = StringUtils.equals(federalChartCode, chartCode); 1627 isFederalAccount &= StringUtils.equals(federalAccountNumber, accountNumber); 1628 isFederalAccount &= StringUtils.equals(federalObjectCode, objectCode); 1629 1630 boolean isStateAccount = StringUtils.equals(stateChartCode, chartCode); 1631 isStateAccount &= StringUtils.equals(stateAccountNumber, accountNumber); 1632 isStateAccount &= StringUtils.equals(stateObjectCode, objectCode); 1633 1634 isTaxAccount = isFederalAccount || isStateAccount; 1635 } 1636 } 1637 1638 return isTaxAccount; 1639 } 1640 1641 public void setParameterService(ParameterService parameterService) { 1642 this.parameterService = parameterService; 1643 } 1644 1645 public void setPurApAccountingDao(PurApAccountingDao purApAccountingDao) { 1646 this.purApAccountingDao = purApAccountingDao; 1647 } 1648 1649 public void setPurapService(PurapService purapService) { 1650 this.purapService = purapService; 1651 } 1652 1653 @Override 1654 public List<SourceAccountingLine> mergeAccountingLineLists(List<SourceAccountingLine> accountingLines1, List<SourceAccountingLine> accountingLines2) { 1655 1656 KualiDecimal totalAmount = KualiDecimal.ZERO; 1657 List<SourceAccountingLine> mergedAccountList = new ArrayList(); 1658 1659 for (SourceAccountingLine line1 : accountingLines1) { 1660 KualiDecimal line1Amount = KualiDecimal.ZERO; 1661 if (ObjectUtils.isNotNull(line1.getAmount())) { 1662 line1Amount = line1.getAmount(); 1663 } 1664 1665 for (SourceAccountingLine line2 : accountingLines2) { 1666 KualiDecimal line2Amount = KualiDecimal.ZERO; 1667 if (ObjectUtils.isNotNull(line2.getAmount())) { 1668 line2Amount = line2.getAmount(); 1669 } 1670 1671 // if we find a match between lists, then merge amounts 1672 if (line1.equals(line2)) { 1673 // add the two amounts 1674 totalAmount = line1Amount.add(line2Amount); 1675 line1.setAmount(totalAmount); 1676 } 1677 } 1678 1679 mergedAccountList.add(line1); 1680 } 1681 1682 return mergedAccountList; 1683 } 1684 1685 /** 1686 * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyPaymentRequestIdentifier(java.lang.Integer) 1687 */ 1688 @Override 1689 public List getSummaryAccountsbyPaymentRequestIdentifier(Integer paymentRequestIdentifier) { 1690 if (paymentRequestIdentifier != null) { 1691 Map fieldValues = new HashMap(); 1692 fieldValues.put(PurapPropertyConstants.PAYMENT_REQUEST_ID, paymentRequestIdentifier); 1693 return new ArrayList(businessObjectService.findMatching(AccountsPayableSummaryAccount.class, fieldValues)); 1694 } 1695 return null; 1696 } 1697 1698 /** 1699 * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyInvoiceIdentifier(java.lang.Integer) 1700 */ 1701 @Override 1702 public List getSummaryAccountsbyInvoiceIdentifier(Integer invoiceIdentifier) { 1703 if (invoiceIdentifier != null) { 1704 Map fieldValues = new HashMap(); 1705 fieldValues.put(PurapPropertyConstants.INVOICE_ID, invoiceIdentifier); 1706 return new ArrayList(businessObjectService.findMatching(OleInvoiceAccountsPayableSummaryAccount.class, fieldValues)); 1707 } 1708 return null; 1709 } 1710 1711 /** 1712 * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyCreditMemoIdentifier(java.lang.Integer) 1713 */ 1714 @Override 1715 public List getSummaryAccountsbyCreditMemoIdentifier(Integer creditMemoIdentifier) { 1716 if (creditMemoIdentifier != null) { 1717 Map fieldValues = new HashMap(); 1718 fieldValues.put(PurapPropertyConstants.CREDIT_MEMO_ID, creditMemoIdentifier); 1719 return new ArrayList(businessObjectService.findMatching(AccountsPayableSummaryAccount.class, fieldValues)); 1720 } 1721 return null; 1722 } 1723 1724 /** 1725 * Sest the businessObjectService. 1726 * 1727 * @param businessObjectService 1728 */ 1729 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 1730 this.businessObjectService = businessObjectService; 1731 } 1732}