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 if(item instanceof OleInvoiceItem) { 423 OleInvoiceItem oleInvoiceItem = (OleInvoiceItem) item; 424 if(!((OleInvoiceItem) item).isDebitItem()) { 425 summaryItem.setEstimatedEncumberanceAmount(amt.negated()); 426 } 427 else { 428 summaryItem.setEstimatedEncumberanceAmount(amt); 429 } 430 } 431 else { 432 summaryItem.setEstimatedEncumberanceAmount(amt); 433 } 434 summaryAccount.getItems().add(summaryItem); 435 break; 436 } 437 } 438 439 } 440 } 441 returnList.add(summaryAccount); 442 } 443 if (LOG.isDebugEnabled()) { 444 LOG.debug(methodName + " ended"); 445 } 446 return returnList; 447 } 448 449 /** 450 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryWithNoZeroTotals(java.util.List) 451 */ 452 @Override 453 public List<SourceAccountingLine> generateSummaryWithNoZeroTotals(List<PurApItem> items) { 454 String methodName = "generateSummaryWithNoZeroTotals()"; 455 if (LOG.isDebugEnabled()) { 456 LOG.debug(methodName + " started"); 457 } 458 List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 459 if (LOG.isDebugEnabled()) { 460 LOG.debug(methodName + " ended"); 461 } 462 return returnList; 463 } 464 465 /** 466 * calls generateSummary with no use tax included 467 */ 468 @Override 469 public List<SourceAccountingLine> generateSummaryWithNoZeroTotalsNoUseTax(List<PurApItem> items) { 470 String methodName = "generateSummaryWithNoZeroTotalsNoUseTax()"; 471 if (LOG.isDebugEnabled()) { 472 LOG.debug(methodName + " started"); 473 } 474 List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_EXCLUDED, false); 475 if (LOG.isDebugEnabled()) { 476 LOG.debug(methodName + " ended"); 477 } 478 479 return returnList; 480 } 481 482 /** 483 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryWithNoZeroTotalsUsingAlternateAmount(java.util.List) 484 */ 485 @Override 486 public List<SourceAccountingLine> generateSummaryWithNoZeroTotalsUsingAlternateAmount(List<PurApItem> items) { 487 String methodName = "generateSummaryWithNoZeroTotals()"; 488 if (LOG.isDebugEnabled()) { 489 LOG.debug(methodName + " started"); 490 } 491 List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_USED, USE_TAX_INCLUDED, false); 492 if (LOG.isDebugEnabled()) { 493 LOG.debug(methodName + " ended"); 494 } 495 return returnList; 496 } 497 498 /** 499 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryExcludeItemTypes(java.util.List, java.util.Set) 500 */ 501 @Override 502 public List<SourceAccountingLine> generateSummaryExcludeItemTypes(List<PurApItem> items, Set excludedItemTypeCodes) { 503 String methodName = "generateSummaryExcludeItemTypes()"; 504 if (LOG.isDebugEnabled()) { 505 LOG.debug(methodName + " started"); 506 } 507 List<SourceAccountingLine> returnList = generateAccountSummary(items, excludedItemTypeCodes, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 508 if (LOG.isDebugEnabled()) { 509 LOG.debug(methodName + " ended"); 510 } 511 return returnList; 512 } 513 514 /** 515 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryIncludeItemTypesAndNoZeroTotals(java.util.List, 516 * java.util.Set) 517 */ 518 @Override 519 public List<SourceAccountingLine> generateSummaryIncludeItemTypesAndNoZeroTotals(List<PurApItem> items, Set includedItemTypeCodes) { 520 String methodName = "generateSummaryExcludeItemTypesAndNoZeroTotals()"; 521 if (LOG.isDebugEnabled()) { 522 LOG.debug(methodName + " started"); 523 } 524 List<SourceAccountingLine> returnList = generateAccountSummary(items, includedItemTypeCodes, ITEM_TYPES_INCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 525 if (LOG.isDebugEnabled()) { 526 LOG.debug(methodName + " ended"); 527 } 528 return returnList; 529 } 530 531 /** 532 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryIncludeItemTypes(java.util.List, java.util.Set) 533 */ 534 @Override 535 public List<SourceAccountingLine> generateSummaryIncludeItemTypes(List<PurApItem> items, Set includedItemTypeCodes) { 536 String methodName = "generateSummaryIncludeItemTypes()"; 537 if (LOG.isDebugEnabled()) { 538 LOG.debug(methodName + " started"); 539 } 540 List<SourceAccountingLine> returnList = generateAccountSummary(items, includedItemTypeCodes, ITEM_TYPES_INCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 541 if (LOG.isDebugEnabled()) { 542 LOG.debug(methodName + " ended"); 543 } 544 return returnList; 545 } 546 547 /** 548 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryExcludeItemTypesAndNoZeroTotals(java.util.List, 549 * java.util.Set) 550 */ 551 @Override 552 public List<SourceAccountingLine> generateSummaryExcludeItemTypesAndNoZeroTotals(List<PurApItem> items, Set excludedItemTypeCodes) { 553 String methodName = "generateSummaryIncludeItemTypesAndNoZeroTotals()"; 554 if (LOG.isDebugEnabled()) { 555 LOG.debug(methodName + " started"); 556 } 557 List<SourceAccountingLine> returnList = generateAccountSummary(items, excludedItemTypeCodes, ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false); 558 if (LOG.isDebugEnabled()) { 559 LOG.debug(methodName + " ended"); 560 } 561 return returnList; 562 } 563 564 /** 565 * Generates an account summary, that is it creates a list of source accounts by rounding up the purap accounts off of the purap 566 * items. 567 * 568 * @param items the items to determ 569 * @param itemTypeCodes the item types to determine whether to look at an item in combination with itemTypeCodesAreIncluded 570 * @param itemTypeCodesAreIncluded value to tell whether the itemTypeCodes parameter lists inclusion or exclusion variables 571 * @param useZeroTotals whether to include items with a zero dollar total 572 * @param useAlternateAmount an alternate amount used in certain cases for GL entry 573 * @return a list of source accounts 574 */ 575 protected List<SourceAccountingLine> generateAccountSummary(List<PurApItem> items, Set<String> itemTypeCodes, Boolean itemTypeCodesAreIncluded, Boolean useZeroTotals, Boolean useAlternateAmount, Boolean useTaxIncluded, Boolean taxableOnly) { 576 List<PurApItem> itemsToProcess = getProcessablePurapItems(items, itemTypeCodes, itemTypeCodesAreIncluded, useZeroTotals); 577 Map<PurApAccountingLine, KualiDecimal> accountMap = new HashMap<PurApAccountingLine, KualiDecimal>(); 578 579 for (PurApItem currentItem : itemsToProcess) { 580 if (PurApItemUtils.checkItemActive(currentItem)) { 581 List<PurApAccountingLine> sourceAccountingLines = currentItem.getSourceAccountingLines(); 582 583 // skip if item is not taxable and taxable only flag has been set 584 if (taxableOnly) { 585 PurchasingAccountsPayableDocument document = currentItem.getPurapDocument(); 586 if (!purapService.isTaxableForSummary(document.isUseTaxIndicator(), purapService.getDeliveryState(document), currentItem)) { 587 continue; 588 } 589 } 590 591 if (!useTaxIncluded) { 592 // if no use tax set the source accounting lines to a clone so we can update 593 // them to be based on the non tax amount 594 if(currentItem instanceof OleInvoiceItem) { 595 OleInvoiceItem invoiceItem = (OleInvoiceItem) currentItem; 596 if(invoiceItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && invoiceItem.getRelatedViews() != null) { 597 // invoiceItem.setRelatedViews(null); 598 PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(invoiceItem); 599 sourceAccountingLines = cloneItem.getSourceAccountingLines(); 600 updateAccountAmountsWithTotal(sourceAccountingLines, invoiceItem.getTotalRemitAmount()); 601 } 602 else { 603 PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(currentItem); 604 sourceAccountingLines = cloneItem.getSourceAccountingLines(); 605 updateAccountAmountsWithTotal(sourceAccountingLines, currentItem.getTotalRemitAmount()); 606 } 607 } 608 else { 609 PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(currentItem); 610 sourceAccountingLines = cloneItem.getSourceAccountingLines(); 611 updateAccountAmountsWithTotal(sourceAccountingLines, currentItem.getTotalRemitAmount()); 612 } 613 } 614 615 for (PurApAccountingLine account : sourceAccountingLines) { 616 617 // skip account if not taxable and taxable only flag is set 618 if (taxableOnly) { 619 PurchasingAccountsPayableDocument document = currentItem.getPurapDocument(); 620 // check if account is not taxable, if not skip this account 621 if (!purapService.isAccountingLineTaxable(account, purapService.isDeliveryStateTaxable(purapService.getDeliveryState(document)))) { 622 continue; 623 } 624 } 625 626 KualiDecimal total = KualiDecimal.ZERO; 627 if (account instanceof InvoiceAccount) { 628 if (((InvoiceAccount) account).getInvoiceItem() != null) { 629 if (((OleInvoiceItem)((InvoiceAccount) account).getInvoiceItem()).isDebitItem()) { 630 if(account.getOrganizationReferenceId()!=null && account.getOrganizationReferenceId().isEmpty()){ 631 account.setOrganizationReferenceId(null); 632 } 633 if (accountMap.containsKey(account)) { 634 total = accountMap.get(account); 635 } 636 637 if (useAlternateAmount) { 638 total = total.add(account.getAlternateAmountForGLEntryCreation()); 639 } else { 640 if (ObjectUtils.isNotNull(account.getAmount())) { 641 total = total.add(account.getAmount()); 642 } 643 } 644 } 645 else { 646 if (accountMap.containsKey(account)) { 647 total = accountMap.get(account); 648 } 649 650 if (useAlternateAmount) { 651 total = total.subtract(account.getAlternateAmountForGLEntryCreation()); 652 } else { 653 if (ObjectUtils.isNotNull(account.getAmount())) { 654 total = total.subtract(account.getAmount()); 655 } 656 } 657 } 658 } 659 660 } 661 else { 662 // getting the total to set on the account 663 if (accountMap.containsKey(account)) { 664 total = accountMap.get(account); 665 } 666 667 if (useAlternateAmount) { 668 total = total.add(account.getAlternateAmountForGLEntryCreation()); 669 } else { 670 if (ObjectUtils.isNotNull(account.getAmount())) { 671 total = total.add(account.getAmount()); 672 } 673 } 674 } 675 accountMap.put(account, total); 676 } 677 } 678 } 679 680 // convert list of PurApAccountingLine objects to SourceAccountingLineObjects 681 Iterator<PurApAccountingLine> iterator = accountMap.keySet().iterator(); 682 List<SourceAccountingLine> sourceAccounts = new ArrayList<SourceAccountingLine>(); 683 for (Iterator<PurApAccountingLine> iter = iterator; iter.hasNext(); ) { 684 PurApAccountingLine accountToConvert = iter.next(); 685 if (accountToConvert.isEmpty()) { 686 String errorMessage = "Found an 'empty' account in summary generation " + accountToConvert.toString(); 687 LOG.error("generateAccountSummary() " + errorMessage); 688 throw new RuntimeException(errorMessage); 689 } 690 KualiDecimal sourceLineTotal = accountMap.get(accountToConvert); 691 SourceAccountingLine sourceLine = accountToConvert.generateSourceAccountingLine(); 692 sourceLine.setAmount(sourceLineTotal); 693 sourceAccounts.add(sourceLine); 694 } 695 696 // sort the sourceAccounts list first by account number, then by object code, ignoring chart code 697 Collections.sort(sourceAccounts, new Comparator<SourceAccountingLine>() { 698 @Override 699 public int compare(SourceAccountingLine sal1, SourceAccountingLine sal2) { 700 int compare = 0; 701 if (sal1 != null && sal2 != null) { 702 if (sal1.getAccountNumber() != null && sal2.getAccountNumber() != null) { 703 compare = sal1.getAccountNumber().compareTo(sal2.getAccountNumber()); 704 if (compare == 0) { 705 if (sal1.getFinancialObjectCode() != null && sal2.getFinancialObjectCode() != null) { 706 compare = sal1.getFinancialObjectCode().compareTo(sal2.getFinancialObjectCode()); 707 } 708 } 709 } 710 } 711 return compare; 712 } 713 }); 714 715 return sourceAccounts; 716 } 717 718 /** 719 * This method takes a list of {@link PurchasingApItem} objects and parses through them to see if each one should be processed 720 * according the the other variables passed in.<br> 721 * <br> 722 * Example 1:<br> 723 * items = "ITEM", "SITM", "FRHT", "SPHD"<br> 724 * itemTypeCodes = "FRHT"<br> 725 * itemTypeCodesAreIncluded = ITEM_TYPES_EXCLUDED_VALUE<br> 726 * return items "ITEM", "SITM", "FRHT", "SPHD"<br> 727 * <br> 728 * <br> 729 * Example 2:<br> 730 * items = "ITEM", "SITM", "FRHT", "SPHD"<br> 731 * itemTypeCodes = "ITEM","FRHT"<br> 732 * itemTypeCodesAreIncluded = ITEM_TYPES_INCLUDED_VALUE<br> 733 * return items "ITEM", "FRHT"<br> 734 * 735 * @param items - list of {@link PurchasingApItem} objects that need to be parsed 736 * @param itemTypeCodes - list of {@link org.kuali.ole.module.purap.businessobject.ItemType} codes used in conjunction with 737 * itemTypeCodesAreIncluded parameter 738 * @param itemTypeCodesAreIncluded - value to tell whether the itemTypeCodes parameter lists inclusion or exclusion variables 739 * (see {@link #ITEM_TYPES_INCLUDED_VALUE}) 740 * @param useZeroTotals - value to tell whether to include zero dollar items (see {@link #ZERO_TOTALS_RETURNED_VALUE}) 741 * @return a list of {@link PurchasingApItem} objects that should be used for processing by calling method 742 */ 743 protected List<PurApItem> getProcessablePurapItems(List<PurApItem> items, Set itemTypeCodes, Boolean itemTypeCodesAreIncluded, Boolean useZeroTotals) { 744 String methodName = "getProcessablePurapItems()"; 745 List<PurApItem> newItemList = new ArrayList<PurApItem>(); 746 // error out if we have an invalid 'itemTypeCodesAreIncluded' value 747 if ((!(ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded))) && (!(ITEM_TYPES_EXCLUDED_VALUE.equals(itemTypeCodesAreIncluded)))) { 748 throwRuntimeException(methodName, "Invalid parameter found while trying to find processable items for dealing with purchasing/accounts payable accounts"); 749 } 750 for (PurApItem currentItem : items) { 751 if ((itemTypeCodes != null) && (!(itemTypeCodes.isEmpty()))) { 752 // we have at least one entry in our item type code list 753 boolean foundMatchInList = false; 754 // check to see if this item type code is in the list 755 for (Iterator iterator = itemTypeCodes.iterator(); iterator.hasNext(); ) { 756 String itemTypeCode = (String) iterator.next(); 757 // include this item if it's in the included list 758 if (itemTypeCode.equals(currentItem.getItemType().getItemTypeCode())) { 759 foundMatchInList = true; 760 break; 761 } 762 } 763 // check to see if item type code was found and if the list is describing included or excluded item types 764 if ((foundMatchInList) && (ITEM_TYPES_EXCLUDED_VALUE.equals(itemTypeCodesAreIncluded))) { 765 // this item type code is in the list 766 // this item type code is excluded so we skip it 767 continue; // skips current item 768 } else if ((!foundMatchInList) && (ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded))) { 769 // this item type code is not in the list 770 // this item type code is not included so we skip it 771 continue; // skips current item 772 } 773 } else { 774 // the item type code list is empty 775 if (ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded)) { 776 // the item type code list is empty and the list is supposed to contain the item types to include 777 throwRuntimeException(methodName, "Invalid parameter and list of items found while trying to find processable items for dealing with purchasing/accounts payable accounts"); 778 } 779 } 780 if ((ZERO_TOTALS_NOT_RETURNED_VALUE.equals(useZeroTotals)) && (ObjectUtils.isNull(currentItem.getExtendedPrice()) || ((KualiDecimal.ZERO.compareTo(currentItem.getExtendedPrice())) == 0))) { 781 // if we don't return zero dollar items then skip this one 782 continue; 783 } 784 newItemList.add(currentItem); 785 } 786 return newItemList; 787 } 788 789 /** 790 * @see org.kuali.ole.module.purap.service.PurapAccountingService#updateAccountAmounts(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument) 791 */ 792 @Override 793 public void updateAccountAmounts(PurchasingAccountsPayableDocument document) { 794 795 PurchasingAccountsPayableDocumentBase purApDocument = (PurchasingAccountsPayableDocumentBase) document; 796 String accountDistributionMethod = purApDocument.getAccountDistributionMethod(); 797 798 KualiRuleService kualiRuleService = SpringContext.getBean(KualiRuleService.class); 799 // olePaymentRequestDocument = (OlePaymentRequestDocument)document; 800 // the percent at fiscal approve 801 // don't update if past the AP review level 802 if ((document instanceof PaymentRequestDocument) && purapService.isFullDocumentEntryCompleted(document)) { 803 // update the percent but don't update the amounts if preq and past full entry 804 convertMoneyToPercent((PaymentRequestDocument) document); 805 return; 806 } 807 if ((document instanceof OleInvoiceDocument) && purapService.isFullDocumentEntryCompleted(document)) { 808 // update the percent but don't update the amounts if preq and past full entry 809 convertMoneyToPercent((OleInvoiceDocument) document); 810 return; 811 } 812 document.fixItemReferences(); 813 814 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 815 //if distribution method is sequential and document is PREQ or VCM then... 816// if (((document instanceof PaymentRequestDocument) 817// || (document instanceof VendorCreditMemoDocument)) 818// && PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) { 819// if (document instanceof VendorCreditMemoDocument) { 820// VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) document; 821// cmDocument.updateExtendedPriceOnItems(); 822// } 823// 824// // update the accounts amounts for PREQ and distribution method = sequential 825// for (PurApItem item : document.getItems()) { 826// updatePreqItemAccountAmounts(item); 827// } 828// 829// return; 830// } 831 832 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 833 //if distribution method is proportional and document is PREQ or VCM then... 834// if (((document instanceof PaymentRequestDocument) || (document instanceof VendorCreditMemoDocument)) && PurapConstants.AccountDistributionMethodCodes.PROPORTIONAL_CODE.equalsIgnoreCase(accountDistributionMethod)) { 835// // update the accounts amounts for PREQ and distribution method = sequential 836// if (document instanceof VendorCreditMemoDocument) { 837// VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) document; 838// cmDocument.updateExtendedPriceOnItems(); 839// } 840 for(PurApItem purApItem:document.getItems()){ 841 for(PurApAccountingLine oldSourceAccountingLine:purApItem.getSourceAccountingLines()) { 842 if(oldSourceAccountingLine instanceof OleRequisitionAccount) { 843 ((OleRequisitionAccount)oldSourceAccountingLine).setExistingAmount(oldSourceAccountingLine.getAmount()); 844 } 845 else if(oldSourceAccountingLine instanceof OlePurchaseOrderAccount) { 846 ((OlePurchaseOrderAccount)oldSourceAccountingLine).setExistingAmount(oldSourceAccountingLine.getAmount()); 847 } 848 else if(oldSourceAccountingLine instanceof PaymentRequestAccount) { 849 ((PaymentRequestAccount)oldSourceAccountingLine).setExistingAmount(oldSourceAccountingLine.getAmount()); 850 } 851 else if(oldSourceAccountingLine instanceof InvoiceAccount) { 852 ((InvoiceAccount)oldSourceAccountingLine).setExistingAmount(oldSourceAccountingLine.getAmount()); 853 } 854 } 855 } 856 for (PurApItem item : document.getItems()) { 857 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 858// boolean rulePassed = true; 859// // check any business rules 860// rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item)); 861// 862// if (rulePassed) { 863// updatePreqProportionalItemAccountAmounts(item); 864 updateItemAccountAmounts(item); 865 if (item instanceof OlePaymentRequestItem) { 866 updateItemAccountAmountsForAdditionalCharge(item, (OlePaymentRequestDocument) document); 867 } else if (item instanceof OleCreditMemoItem) { 868 updateItemAccountAmountsForAdditionalCharge(item, (OleVendorCreditMemoDocument) document); 869 } else if (item instanceof OleInvoiceItem) { 870 updateItemAccountAmountsForAdditionalCharge(item, (OleInvoiceDocument) document); 871 } else { 872 updateItemAccountAmounts(item); 873// updatePreqProportionalItemAccountAmounts(item); 874 } 875// } 876 } 877 878 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 879// return; 880// } 881 882 // OLE-3405 : disabling KFS Proportional/sequential distribution methods 883 //No recalculate if the account distribution method code is equal to "S" sequential ON REQ or POs.. 884// if (PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) { 885// for (PurApItem item : document.getItems()) { 886// boolean rulePassed = true; 887// // check any business rules 888// rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item)); 889// 890// return; 891// } 892// } 893// 894// //do recalculate only if the account distribution method code is not equal to "S" sequential method. 895// if (!PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) { 896// for (PurApItem item : document.getItems()) { 897// boolean rulePassed = true; 898// // check any business rules 899// rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item)); 900// 901// if (rulePassed) { 902// updateItemAccountAmounts(item); 903// } 904// } 905// } 906 } 907 908 public void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OlePaymentRequestDocument document) { 909 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 910 OlePaymentRequestItem olePaymentRequestItem = (OlePaymentRequestItem) item; 911 KualiDecimal prorateSurcharge = new KualiDecimal(); 912 KualiDecimal qty = KualiDecimal.ZERO; 913 if (olePaymentRequestItem.getItemSurcharge() == null) { 914 prorateSurcharge = KualiDecimal.ZERO; 915 } else { 916 qty = olePaymentRequestItem.getItemQuantity(); 917 prorateSurcharge = new KualiDecimal(olePaymentRequestItem.getItemSurcharge()); 918 } 919 KualiDecimal totalAmount = item.getTotalAmount(); 920 totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty)); 921 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 922 } 923 924 public void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OleInvoiceDocument document) { 925 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 926 OleInvoiceItem oleInvoiceItem = (OleInvoiceItem) item; 927 KualiDecimal prorateSurcharge = new KualiDecimal(); 928 KualiDecimal qty = KualiDecimal.ZERO; 929 if (oleInvoiceItem.getItemSurcharge() == null) { 930 prorateSurcharge = KualiDecimal.ZERO; 931 } else { 932 qty = oleInvoiceItem.getItemQuantity(); 933 prorateSurcharge = new KualiDecimal(oleInvoiceItem.getItemSurcharge()); 934 } 935 KualiDecimal totalAmount = item.getTotalAmount(); 936 totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty)); 937 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 938 } 939 940 private void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OleVendorCreditMemoDocument document) { 941 942 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 943 OleCreditMemoItem oleCreditMemoItem = (OleCreditMemoItem) item; 944 KualiDecimal prorateSurcharge = new KualiDecimal(); 945 KualiDecimal qty = KualiDecimal.ZERO; 946 if (oleCreditMemoItem.getItemSurcharge() == null) { 947 prorateSurcharge = KualiDecimal.ZERO; 948 } else { 949 qty = oleCreditMemoItem.getItemQuantity(); 950 prorateSurcharge = new KualiDecimal(oleCreditMemoItem.getItemSurcharge()); 951 } 952 KualiDecimal totalAmount = item.getTotalAmount(); 953 totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty)); 954 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 955 956 } 957 958 /** 959 * @see org.kuali.ole.module.purap.service.PurapAccountingService#updateItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem) 960 */ 961 @Override 962 public void updateItemAccountAmounts(PurApItem item) { 963 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 964 KualiDecimal totalAmount = item.getTotalAmount(); 965 if (item.getItemType().isAdditionalChargeIndicator()) { 966 updateAccountAmountsWithTotalForAdditionalCharge(sourceAccountingLines, totalAmount); 967 } else { 968 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 969 } 970 } 971 972 /*public void updateItemAccountAmountsForAdditionalCharge(PurApItem item) { 973 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 974 KualiDecimal totalAmount = item.getTotalAmount(); 975 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 976 }*/ 977 978 /** 979 * calculates values for a list of accounting lines based on an amount 980 * 981 * @param sourceAccountingLines 982 * @param totalAmount 983 */ 984 985 public <T extends PurApAccountingLine> void updateAccountAmountsWithTotalForAdditionalCharge(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 986 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 987 988 KualiDecimal accountTotal = KualiDecimal.ZERO; 989 T lastAccount = null; 990 991 992 for (T account : sourceAccountingLines) { 993 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 994 BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString()).divide(new BigDecimal(100)); 995 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString())).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 996 } else { 997 account.setAmount(KualiDecimal.ZERO); 998 } 999 accountTotal = accountTotal.add(account.getAmount()); 1000 lastAccount = account; 1001 } 1002 1003 // put excess on last account 1004 if (lastAccount != null) { 1005 KualiDecimal difference = totalAmount.subtract(accountTotal); 1006 lastAccount.setAmount(lastAccount.getAmount().add(difference)); 1007 } 1008 } else { 1009 // zero out if extended price is zero 1010 for (T account : sourceAccountingLines) { 1011 account.setAmount(KualiDecimal.ZERO); 1012 } 1013 } 1014 } 1015 1016 public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal2(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 1017 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 1018 1019 KualiDecimal accountTotal = KualiDecimal.ZERO; 1020 T lastAccount = null; 1021 1022 1023 for (T account : sourceAccountingLines) { 1024 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1025 // OlePaymentRequestItem item = new OlePaymentRequestItem(); 1026 BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString()).divide(new BigDecimal(100)); 1027 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString())).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 1028 } else { 1029 account.setAmount(KualiDecimal.ZERO); 1030 } 1031 accountTotal = accountTotal.add(account.getAmount()); 1032 lastAccount = account; 1033 } 1034 1035 // put excess on last account 1036 if (lastAccount != null) { 1037 KualiDecimal difference = totalAmount.subtract(accountTotal); 1038 lastAccount.setAmount(lastAccount.getAmount().add(difference)); 1039 } 1040 } else { 1041 // zero out if extended price is zero 1042 for (T account : sourceAccountingLines) { 1043 account.setAmount(KualiDecimal.ZERO); 1044 } 1045 } 1046 } 1047 1048 /** 1049 * calculates values for a list of accounting lines based on an amount 1050 * 1051 * @param sourceAccountingLines 1052 * @param totalAmount 1053 */ 1054 @Override 1055 public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 1056 updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount, new KualiDecimal(0)); 1057 } 1058 1059 /** 1060 * calculates values for a list of accounting lines based on an amount taking discount into account 1061 * 1062 * @param sourceAccountingLines 1063 * @param totalAmount 1064 * @param discountAmount 1065 */ 1066 @Override 1067 public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal(List<T> sourceAccountingLines, 1068 KualiDecimal totalAmount, KualiDecimal discountAmount) { 1069 1070 // if we have a discount, then we need to base the amounts on the discount, but the percent on the total 1071 boolean noDiscount = true; 1072 if ((discountAmount != null) && KualiDecimal.ZERO.compareTo(discountAmount) != 0) { 1073 noDiscount = false; 1074 } 1075 1076 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 1077 1078 KualiDecimal accountTotal = KualiDecimal.ZERO; 1079 BigDecimal accountTotalPercent = BigDecimal.ZERO; 1080 T lastAccount = null; 1081 1082 for (T account : sourceAccountingLines) { 1083 if (ObjectUtils.isNotNull(account.getAccountLinePercent()) || ObjectUtils.isNotNull(account.getAmount())) { 1084 if (ObjectUtils.isNotNull(account.getAmount()) && account.getAmount().isGreaterThan(KualiDecimal.ZERO)) { 1085 KualiDecimal amt = account.getAmount(); 1086 KualiDecimal calculatedPercent = new KualiDecimal(amt.multiply(new KualiDecimal(100)).divide(totalAmount).toString()); 1087 account.setAccountLinePercent(calculatedPercent.bigDecimalValue().setScale(BIG_DECIMAL_SCALE)); 1088 } 1089 1090 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1091 BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString()).divide(new BigDecimal(100)); 1092 if (noDiscount) { 1093 if (ObjectUtils.isNull(account.getAmount()) || account.getAmount().isZero()) { 1094 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString())) 1095 .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 1096 } 1097 } else { 1098 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(discountAmount.toString())) 1099 .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 1100 } 1101 } 1102 } 1103 1104 if (ObjectUtils.isNotNull(account.getAmount())) { 1105 accountTotal = accountTotal.add(account.getAmount()); 1106 } 1107 1108 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1109 accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent()); 1110 } 1111 1112 lastAccount = account; 1113 } 1114 1115 // put excess on last account 1116 if (lastAccount != null) { 1117 KualiDecimal difference = new KualiDecimal(0); 1118 if (noDiscount) { 1119 difference = totalAmount.subtract(accountTotal); 1120 } else { 1121 difference = discountAmount.subtract(accountTotal); 1122 } 1123 if (ObjectUtils.isNotNull(lastAccount.getAmount())) { 1124 if((difference.abs()).isLessEqual(new KualiDecimal(1).multiply(new KualiDecimal(sourceAccountingLines.size()).divide(new KualiDecimal(2))))) { 1125 lastAccount.setAmount(lastAccount.getAmount().add(difference)); 1126 } 1127 else { 1128 lastAccount.setAmount(lastAccount.getAmount()); 1129 } 1130 } 1131 BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent).setScale(BIG_DECIMAL_SCALE,BigDecimal.ROUND_CEILING); 1132 if (ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) { 1133 1134 KualiDecimal differencePercent = (((new KualiDecimal(accountTotalPercent)).subtract(new KualiDecimal(100))).abs()); 1135 if((differencePercent.abs()).isLessEqual(new KualiDecimal(1).multiply((new KualiDecimal(sourceAccountingLines.size()).divide(new KualiDecimal(2)))))) { 1136 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference)); 1137 } 1138 else { 1139 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent()); 1140 } 1141 } 1142 } 1143 } else { 1144 // zero out if extended price is zero 1145 for (T account : sourceAccountingLines) { 1146 if (ObjectUtils.isNotNull(account.getAmount())) { 1147 account.setAmount(KualiDecimal.ZERO); 1148 } 1149 } 1150 } 1151 } 1152 1153 /** 1154 * @see org.kuali.ole.module.purap.service.PurapAccountingService#updatePreqProportionalItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem) 1155 */ 1156 @Override 1157 public void updatePreqProportionalItemAccountAmounts(PurApItem item) { 1158 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 1159 KualiDecimal totalAmount = item.getTotalAmount(); 1160 1161 updatePreqProporationalAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 1162 } 1163 1164 /** 1165 * calculates values for a list of accounting lines based on an amount for proportional method 1166 * 1167 * @param sourceAccountingLines 1168 * @param totalAmount 1169 */ 1170 @Override 1171 public <T extends PurApAccountingLine> void updatePreqProporationalAccountAmountsWithTotal(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 1172 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 1173 KualiDecimal accountTotal = KualiDecimal.ZERO; 1174 BigDecimal accountTotalPercent = BigDecimal.ZERO; 1175 T lastAccount = null; 1176 1177 for (T account : sourceAccountingLines) { 1178 if (ObjectUtils.isNotNull(account.getAccountLinePercent()) || ObjectUtils.isNotNull(account.getAmount())) { 1179 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1180 BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString()).divide(new BigDecimal(100)); 1181 account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString())).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 1182 } 1183 } 1184 1185 if (ObjectUtils.isNotNull(account.getAmount())) { 1186 accountTotal = accountTotal.add(account.getAmount()); 1187 } 1188 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1189 accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent()); 1190 } 1191 1192 lastAccount = account; 1193 } 1194 1195 // put excess on last account 1196 if (lastAccount != null) { 1197 KualiDecimal difference = totalAmount.subtract(accountTotal); 1198 if (ObjectUtils.isNotNull(lastAccount.getAmount())) { 1199 lastAccount.setAmount(lastAccount.getAmount().add(difference)); 1200 } 1201 1202 BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent).setScale(BIG_DECIMAL_SCALE,BigDecimal.ROUND_CEILING); 1203 if (ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) { 1204 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference)); 1205 } 1206 } 1207 } 1208 } 1209 1210 /** 1211 * @see org.kuali.ole.module.purap.service.PurapAccountingService#updatePreqItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem) 1212 */ 1213 @Override 1214 public void updatePreqItemAccountAmounts(PurApItem item) { 1215 List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines(); 1216 KualiDecimal totalAmount = item.getTotalAmount(); 1217 1218 updatePreqAccountAmountsWithTotal(sourceAccountingLines, totalAmount); 1219 } 1220 1221 /** 1222 * calculates values for a list of accounting lines based on an amount. Preq item's extended 1223 * cost is distributed to the accounting lines. 1224 * 1225 * @param sourceAccountingLines 1226 * @param totalAmount 1227 */ 1228 @Override 1229 public <T extends PurApAccountingLine> void updatePreqAccountAmountsWithTotal(List<T> sourceAccountingLines, KualiDecimal totalAmount) { 1230 if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) { 1231 KualiDecimal accountTotal = KualiDecimal.ZERO; 1232 BigDecimal accountTotalPercent = BigDecimal.ZERO; 1233 T lastAccount = null; 1234 1235 for (T account : sourceAccountingLines) { 1236 //look at lines where amount is non-zero.. 1237 if (account.getAmount().isGreaterThan(KualiDecimal.ZERO)) { 1238 if (totalAmount.isZero()) { 1239 account.setAmount(KualiDecimal.ZERO); 1240 } else { 1241 if (account.getAmount().isGreaterThan(totalAmount)) { 1242 account.setAmount(totalAmount); 1243 } 1244 } 1245 } 1246 1247 totalAmount = totalAmount.subtract(account.getAmount()); 1248 } 1249 1250 if (totalAmount.isGreaterThan(KualiDecimal.ZERO)) { 1251 for (T account : sourceAccountingLines) { 1252 if (account.getAmount().isZero() || account.getAccountLinePercent().compareTo(BigDecimal.ZERO) == 1) { 1253 KualiDecimal priorAmount = account.getAmount(); 1254 account.setAmount(account.getAmount().add(new KualiDecimal(account.getAccountLinePercent()).multiply(totalAmount).divide(new KualiDecimal(100)))); 1255 accountTotal = accountTotal.add(account.getAmount().subtract(priorAmount)); 1256 lastAccount = account; 1257 } 1258 } 1259 } 1260 1261 accountTotal = totalAmount.subtract(accountTotal); 1262 1263 if (accountTotal.isGreaterThan(KualiDecimal.ZERO) && ObjectUtils.isNotNull(lastAccount)) { 1264 //add the difference to the last overage account.... 1265 lastAccount.setAmount(lastAccount.getAmount().add(accountTotal)); 1266 } 1267 } 1268 } 1269 1270 public List<PurApAccountingLine> generatePercentSummary(PurchasingAccountsPayableDocument purapDoc) { 1271 List<PurApAccountingLine> accounts = new ArrayList<PurApAccountingLine>(); 1272 for (PurApItem currentItem : purapDoc.getItems()) { 1273 if (PurApItemUtils.checkItemActive(currentItem)) { 1274 for (PurApAccountingLine account : currentItem.getSourceAccountingLines()) { 1275 boolean thisAccountAlreadyInSet = false; 1276 for (Object element : accounts) { 1277 PurApAccountingLine alreadyAddedAccount = (PurApAccountingLine) element; 1278 1279 1280 if (alreadyAddedAccount.accountStringsAreEqual(account)) { 1281 BigDecimal alreadyAddedAccountLinePercent = BigDecimal.ZERO; 1282 if (ObjectUtils.isNotNull(alreadyAddedAccount.getAccountLinePercent())) { 1283 alreadyAddedAccountLinePercent = alreadyAddedAccount.getAccountLinePercent(); 1284 } 1285 BigDecimal accountLinePercent = BigDecimal.ZERO; 1286 if (ObjectUtils.isNotNull(account.getAccountLinePercent())) { 1287 accountLinePercent = account.getAccountLinePercent(); 1288 } 1289 1290 alreadyAddedAccount.setAccountLinePercent(alreadyAddedAccountLinePercent.add(accountLinePercent)); 1291 1292 thisAccountAlreadyInSet = true; 1293 break; 1294 } 1295 } 1296 if (!thisAccountAlreadyInSet) { 1297 PurApAccountingLine accountToAdd = (PurApAccountingLine) ObjectUtils.deepCopy(account); 1298 accounts.add(accountToAdd); 1299 } 1300 } 1301 } 1302 } 1303 return accounts; 1304 } 1305 1306 /** 1307 * @see org.kuali.ole.module.purap.service.PurapAccountingService#convertMoneyToPercent(org.kuali.ole.module.purap.document.PaymentRequestDocument) 1308 */ 1309 @Override 1310 public void convertMoneyToPercent(PaymentRequestDocument pr) { 1311 LOG.debug("convertMoneyToPercent() started"); 1312 1313 int itemNbr = 0; 1314 1315 for (Iterator<PaymentRequestItem> iter = pr.getItems().iterator(); iter.hasNext(); ) { 1316 PaymentRequestItem item = iter.next(); 1317 1318 itemNbr++; 1319 String identifier = item.getItemIdentifierString(); 1320 1321 if (item.getTotalAmount() != null && item.getTotalAmount().isNonZero()) { 1322 int numOfAccounts = item.getSourceAccountingLines().size(); 1323 BigDecimal percentTotal = BigDecimal.ZERO; 1324 KualiDecimal accountTotal = KualiDecimal.ZERO; 1325 int accountIdentifier = 0; 1326 1327 KualiDecimal addChargeItem = KualiDecimal.ZERO; 1328 KualiDecimal lineItemPreTaxTotal = KualiDecimal.ZERO; 1329 /* KualiDecimal prorateSurcharge = KualiDecimal.ZERO; 1330 if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice() != null && item.getExtendedPrice().compareTo(KualiDecimal.ZERO) != 0) { 1331 if (((OlePaymentRequestItem) item).getItemSurcharge() != null) { 1332 prorateSurcharge = new KualiDecimal(((OlePaymentRequestItem) item).getItemSurcharge()).multiply(item.getItemQuantity()); 1333 } 1334 }*/ 1335 PurApAccountingLine lastAccount = null; 1336 BigDecimal accountTotalPercent = BigDecimal.ZERO; 1337 1338 for (PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { 1339 accountIdentifier++; 1340 PaymentRequestAccount account = (PaymentRequestAccount) purApAccountingLine; 1341 1342 // account.getAmount returns the wrong value for trade in source accounting lines... 1343 KualiDecimal accountAmount = KualiDecimal.ZERO; 1344 if (ObjectUtils.isNotNull(account.getAmount())) { 1345 accountAmount = account.getAmount(); 1346 } 1347 1348 BigDecimal tmpPercent = BigDecimal.ZERO; 1349 KualiDecimal extendedPrice = item.getTotalAmount(); 1350 tmpPercent = accountAmount.bigDecimalValue().divide(extendedPrice.bigDecimalValue(), PurapConstants.CREDITMEMO_PRORATION_SCALE.intValue(), KualiDecimal.ROUND_BEHAVIOR); 1351 1352 if (accountIdentifier == numOfAccounts) { 1353 // if on last account, calculate the percent by subtracting current percent total from 1 1354 tmpPercent = BigDecimal.ONE.subtract(percentTotal); 1355 } 1356 1357 // test that the above amount is correct, if so just check that the total of all these matches the item total 1358 BigDecimal calcAmountBd = tmpPercent.multiply(extendedPrice.bigDecimalValue()); 1359 calcAmountBd = calcAmountBd.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR); 1360 KualiDecimal calcAmount = new KualiDecimal(calcAmountBd); 1361 //calcAmount = calcAmount.subtract(prorateSurcharge); 1362 if (calcAmount.compareTo(accountAmount) != 0) { 1363 // rounding error 1364 if (LOG.isDebugEnabled()) { 1365 LOG.debug("convertMoneyToPercent() Rounding error on " + account); 1366 } 1367 String param1 = identifier + "." + accountIdentifier; 1368 String param2 = calcAmount.bigDecimalValue().subtract(accountAmount.bigDecimalValue()).toString(); 1369 GlobalVariables.getMessageMap().putError(item.getItemIdentifierString(), PurapKeyConstants.ERROR_ITEM_ACCOUNTING_ROUNDING, param1, param2); 1370 account.setAmount(calcAmount); 1371 } 1372 1373 // update percent 1374 if (LOG.isDebugEnabled()) { 1375 LOG.debug("convertMoneyToPercent() updating percent to " + tmpPercent); 1376 } 1377 account.setAccountLinePercent(tmpPercent.multiply(new BigDecimal(100))); 1378 accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent()); 1379 lastAccount = account; 1380 // check total based on adjusted amount 1381 accountTotal = accountTotal.add(calcAmount); 1382 percentTotal = percentTotal.add(tmpPercent); 1383 } 1384 BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent).setScale(BIG_DECIMAL_SCALE,BigDecimal.ROUND_CEILING); 1385 if (ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) { 1386 KualiDecimal differencePercent = (((new KualiDecimal(accountTotalPercent)).subtract(new KualiDecimal(100))).abs()); 1387 if((differencePercent.abs()).isLessEqual(new KualiDecimal(1).multiply((new KualiDecimal(item.getSourceAccountingLines().size()).divide(new KualiDecimal(2)))))) { 1388 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference)); 1389 } 1390 else { 1391 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent()); 1392 } 1393 } 1394 } 1395 } 1396 } 1397 1398 1399 @Override 1400 public void convertMoneyToPercent(InvoiceDocument inv) { 1401 LOG.debug("convertMoneyToPercent() started"); 1402 1403 int itemNbr = 0; 1404 1405 for (Iterator<InvoiceItem> iter = inv.getItems().iterator(); iter.hasNext(); ) { 1406 InvoiceItem item = iter.next(); 1407 1408 itemNbr++; 1409 String identifier = item.getItemIdentifierString(); 1410 1411 if (item.getTotalAmount() != null && item.getTotalAmount().isNonZero()) { 1412 int numOfAccounts = item.getSourceAccountingLines().size(); 1413 BigDecimal percentTotal = BigDecimal.ZERO; 1414 KualiDecimal accountTotal = KualiDecimal.ZERO; 1415 int accountIdentifier = 0; 1416 1417 KualiDecimal addChargeItem = KualiDecimal.ZERO; 1418 KualiDecimal lineItemPreTaxTotal = KualiDecimal.ZERO; 1419 KualiDecimal prorateSurcharge = KualiDecimal.ZERO; 1420 if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice() != null && item.getExtendedPrice().compareTo(KualiDecimal.ZERO) != 0) { 1421 if (((OleInvoiceItem) item).getItemSurcharge() != null) { 1422 prorateSurcharge = new KualiDecimal(((OleInvoiceItem) item).getItemSurcharge()).multiply(item.getItemQuantity()); 1423 } 1424 } 1425 PurApAccountingLine lastAccount = null; 1426 BigDecimal accountTotalPercent = BigDecimal.ZERO; 1427 for (PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { 1428 accountIdentifier++; 1429 InvoiceAccount account = (InvoiceAccount) purApAccountingLine; 1430 1431 // account.getAmount returns the wrong value for trade in source accounting lines... 1432 KualiDecimal accountAmount = KualiDecimal.ZERO; 1433 if (ObjectUtils.isNotNull(account.getAmount())) { 1434 accountAmount = account.getAmount(); 1435 } 1436 1437 BigDecimal tmpPercent = BigDecimal.ZERO; 1438 KualiDecimal extendedPrice = item.getTotalAmount(); 1439 tmpPercent = accountAmount.bigDecimalValue().divide(extendedPrice.bigDecimalValue(), PurapConstants.CREDITMEMO_PRORATION_SCALE.intValue(), KualiDecimal.ROUND_BEHAVIOR); 1440 1441 if (accountIdentifier == numOfAccounts) { 1442 // if on last account, calculate the percent by subtracting current percent total from 1 1443 tmpPercent = BigDecimal.ONE.subtract(percentTotal); 1444 } 1445 1446 // test that the above amount is correct, if so just check that the total of all these matches the item total 1447 BigDecimal calcAmountBd = tmpPercent.multiply(extendedPrice.bigDecimalValue()); 1448 calcAmountBd = calcAmountBd.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR); 1449 KualiDecimal calcAmount = new KualiDecimal(calcAmountBd); 1450 calcAmount = calcAmount.subtract(prorateSurcharge); 1451 if (calcAmount.compareTo(accountAmount) != 0) { 1452 // rounding error 1453 if (LOG.isDebugEnabled()) { 1454 LOG.debug("convertMoneyToPercent() Rounding error on " + account); 1455 } 1456 String param1 = identifier + "." + accountIdentifier; 1457 String param2 = calcAmount.bigDecimalValue().subtract(accountAmount.bigDecimalValue()).toString(); 1458 GlobalVariables.getMessageMap().putError(item.getItemIdentifierString(), PurapKeyConstants.ERROR_ITEM_ACCOUNTING_ROUNDING, param1, param2); 1459 account.setAmount(calcAmount); 1460 } 1461 1462 // update percent 1463 if (LOG.isDebugEnabled()) { 1464 LOG.debug("convertMoneyToPercent() updating percent to " + tmpPercent); 1465 } 1466 account.setAccountLinePercent(tmpPercent.multiply(new BigDecimal(100))); 1467 accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent()); 1468 lastAccount = account; 1469 1470 // check total based on adjusted amount 1471 accountTotal = accountTotal.add(calcAmount); 1472 percentTotal = percentTotal.add(tmpPercent); 1473 } 1474 BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent).setScale(BIG_DECIMAL_SCALE,BigDecimal.ROUND_CEILING); 1475 if (ObjectUtils.isNotNull(lastAccount) && ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) { 1476 KualiDecimal differencePercent = (((new KualiDecimal(accountTotalPercent)).subtract(new KualiDecimal(100))).abs()); 1477 if((differencePercent.abs()).isLessEqual(new KualiDecimal(1).multiply((new KualiDecimal(item.getSourceAccountingLines().size()).divide(new KualiDecimal(2)))))) { 1478 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference)); 1479 } 1480 else { 1481 lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent()); 1482 } 1483 } 1484 } 1485 } 1486 } 1487 1488 /** 1489 * @see org.kuali.ole.module.purap.service.PurapAccountingService#deleteSummaryAccounts(java.lang.Integer, java.lang.String) 1490 */ 1491 @Override 1492 public void deleteSummaryAccounts(Integer purapDocumentIdentifier, String docType) { 1493 if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) { 1494 purApAccountingDao.deleteSummaryAccountsbyPaymentRequestIdentifier(purapDocumentIdentifier); 1495 } else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) { 1496 purApAccountingDao.deleteSummaryAccountsbyCreditMemoIdentifier(purapDocumentIdentifier); 1497 } else if (PurapDocTypeCodes.INVOICE_DOCUMENT.equals(docType)) { 1498 purApAccountingDao.deleteSummaryAccountsbyInvoiceIdentifier(purapDocumentIdentifier); 1499 } 1500 } 1501 1502 @Override 1503 public List getAccountsPayableSummaryAccounts(Integer purapDocumentIdentifier, String docType) { 1504 if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) { 1505 return getSummaryAccountsbyPaymentRequestIdentifier(purapDocumentIdentifier); 1506 } else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) { 1507 getSummaryAccountsbyCreditMemoIdentifier(purapDocumentIdentifier); 1508 } else if (PurapDocTypeCodes.INVOICE_DOCUMENT.equals(docType)) { 1509 getSummaryAccountsbyInvoiceIdentifier(purapDocumentIdentifier); 1510 } 1511 return null; 1512 } 1513 1514 @Override 1515 public List<PurApAccountingLine> getAccountsFromItem(PurApItem item) { 1516 return purApAccountingDao.getAccountingLinesForItem(item); 1517 } 1518 1519 @Override 1520 public List<SourceAccountingLine> generateSourceAccountsForVendorRemit(PurchasingAccountsPayableDocument document) { 1521 // correct initial amounts or percents 1522 //updateAccountAmounts(document); 1523 List<SourceAccountingLine> vendorSummaryAccounts = new ArrayList<SourceAccountingLine>(); 1524 1525 // update accounts here with amounts to send to vendor 1526 vendorSummaryAccounts = generateSummaryWithNoZeroTotalsNoUseTax(document.getItems()); 1527 1528 return vendorSummaryAccounts; 1529 } 1530 1531 /** 1532 * gets sum total of accounts 1533 * 1534 * @param accounts 1535 * @return 1536 */ 1537 1538 protected KualiDecimal calculateSumTotal(List<SourceAccountingLine> accounts) { 1539 KualiDecimal total = KualiDecimal.ZERO; 1540 for (SourceAccountingLine accountingLine : accounts) { 1541 KualiDecimal amt = KualiDecimal.ZERO; 1542 if (ObjectUtils.isNotNull(accountingLine.getAmount())) { 1543 amt = accountingLine.getAmount(); 1544 } 1545 total = total.add(amt); 1546 } 1547 return total; 1548 } 1549 1550 /** 1551 * Replaces amount field with prorated tax amount in list 1552 * 1553 * @param accounts list of accounts 1554 * @param useTax tax to be allocated to these accounts 1555 * @param newSourceLines rewrites the source account lines 1556 */ 1557 1558 protected void convertAmtToTax(List<PurApAccountingLine> accounts, KualiDecimal useTax, List<SourceAccountingLine> newSourceLines) { 1559 final BigDecimal HUNDRED = new BigDecimal(100); 1560 PurApAccountingLine purApAccountingLine; 1561 BigDecimal proratedAmtBD; 1562 KualiDecimal proratedAmt; 1563 // convert back to source 1564 KualiDecimal total = KualiDecimal.ZERO; 1565 int last = accounts.size() - 1; 1566 for (int i = 0; i < last; i++) { 1567 purApAccountingLine = accounts.get(i); 1568 BigDecimal linePercent = BigDecimal.ZERO; 1569 if (ObjectUtils.isNotNull(purApAccountingLine.getAccountLinePercent())) { 1570 linePercent = purApAccountingLine.getAccountLinePercent(); 1571 } 1572 1573 proratedAmtBD = useTax.bigDecimalValue().multiply(linePercent); 1574 // last object takes the rest of the amount 1575 // proratedAmt = (accounts.indexOf(purApAccountingLine) == last) ? useTax.subtract(total) : proratedAmt.divide(HUNDRED); 1576 proratedAmtBD = proratedAmtBD.divide(HUNDRED); 1577 proratedAmt = new KualiDecimal(proratedAmtBD); 1578 SourceAccountingLine acctLine = purApAccountingLine.generateSourceAccountingLine(); 1579 acctLine.setAmount(proratedAmt); 1580 newSourceLines.add(acctLine); 1581 total = total.add(proratedAmt); 1582 } 1583 // update last object with remaining balance 1584 proratedAmt = useTax.subtract(total); 1585 purApAccountingLine = accounts.get(last); 1586 SourceAccountingLine acctLine = purApAccountingLine.generateSourceAccountingLine(); 1587 acctLine.setAmount(proratedAmt); 1588 newSourceLines.add(acctLine); 1589 } 1590 1591 /** 1592 * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateUseTaxAccount(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument) 1593 */ 1594 @Override 1595 public List<UseTaxContainer> generateUseTaxAccount(PurchasingAccountsPayableDocument document) { 1596 List<UseTaxContainer> useTaxAccounts = new ArrayList<UseTaxContainer>(); 1597 1598 HashMap<PurApItemUseTax, UseTaxContainer> useTaxItemMap = new HashMap<PurApItemUseTax, UseTaxContainer>(); 1599 Class accountingLineClass = null; 1600 if (!document.isUseTaxIndicator()) { 1601 // not useTax, return 1602 return useTaxAccounts; 1603 } 1604 for (PurApItem purApItem : document.getItems()) { 1605 if (!purApItem.getUseTaxItems().isEmpty()) { 1606 if (accountingLineClass == null) { 1607 accountingLineClass = purApItem.getAccountingLineClass(); 1608 } 1609 UseTaxContainer useTaxContainer = new UseTaxContainer(); 1610 for (PurApItemUseTax itemUseTax : purApItem.getUseTaxItems()) { 1611 if (useTaxItemMap.containsKey(itemUseTax)) { 1612 useTaxContainer = useTaxItemMap.get(itemUseTax); 1613 PurApItemUseTax exisitingItemUseTax = useTaxContainer.getUseTax(); 1614 // if already in set we need to add on the old amount 1615 KualiDecimal tax = exisitingItemUseTax.getTaxAmount(); 1616 tax = tax.add(itemUseTax.getTaxAmount()); 1617 exisitingItemUseTax.setTaxAmount(tax); 1618 1619 List<PurApItem> items = useTaxContainer.getItems(); 1620 items.add(purApItem); 1621 useTaxContainer.setItems(items); 1622 1623 } else { 1624 useTaxContainer = new UseTaxContainer(itemUseTax, purApItem); 1625 useTaxItemMap.put(itemUseTax, useTaxContainer); 1626 useTaxAccounts.add(useTaxContainer); 1627 } 1628 } 1629 } 1630 } 1631 // iterate over useTaxAccounts and set summary accounts using proration 1632 for (UseTaxContainer useTaxContainer : useTaxAccounts) { 1633 1634 // create summary from items 1635 List<SourceAccountingLine> origSourceAccounts = this.generateSummaryWithNoZeroTotals(useTaxContainer.getItems()); 1636 KualiDecimal totalAmount = calculateSumTotal(origSourceAccounts); 1637 List<PurApAccountingLine> accountingLines = generateAccountDistributionForProration(origSourceAccounts, totalAmount, PurapConstants.PRORATION_SCALE, accountingLineClass); 1638 1639 1640 List<SourceAccountingLine> newSourceLines = new ArrayList<SourceAccountingLine>(); 1641 // convert back to source 1642 convertAmtToTax(accountingLines, useTaxContainer.getUseTax().getTaxAmount(), newSourceLines); 1643 1644 // do we need an update accounts here? 1645 useTaxContainer.setAccounts(newSourceLines); 1646 } 1647 1648 useTaxAccounts = new ArrayList<UseTaxContainer>(useTaxItemMap.values()); 1649 return useTaxAccounts; 1650 } 1651 1652 /** 1653 * @see org.kuali.ole.module.purap.service.PurapAccountingService#isTaxAccount(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument, 1654 * org.kuali.ole.sys.businessobject.SourceAccountingLine) 1655 */ 1656 @Override 1657 public boolean isTaxAccount(PurchasingAccountsPayableDocument document, SourceAccountingLine account) { 1658 boolean isTaxAccount = false; 1659 1660 // check if the summary account is for tax withholding 1661 if (document instanceof PaymentRequestDocument) { 1662 String incomeClassCode = ((PaymentRequestDocument) document).getTaxClassificationCode(); 1663 if (StringUtils.isNotEmpty(incomeClassCode)) { 1664 1665 String federalChartCode = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX); 1666 String federalAccountNumber = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX); 1667 String federalObjectCode = parameterService.getSubParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX, incomeClassCode); 1668 1669 String stateChartCode = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX); 1670 String stateAccountNumber = parameterService.getParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX); 1671 String stateObjectCode = parameterService.getSubParameterValueAsString(PaymentRequestDocument.class, NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX, incomeClassCode); 1672 1673 String chartCode = account.getChartOfAccountsCode(); 1674 String accountNumber = account.getAccountNumber(); 1675 String objectCode = account.getFinancialObjectCode(); 1676 1677 boolean isFederalAccount = StringUtils.equals(federalChartCode, chartCode); 1678 isFederalAccount &= StringUtils.equals(federalAccountNumber, accountNumber); 1679 isFederalAccount &= StringUtils.equals(federalObjectCode, objectCode); 1680 1681 boolean isStateAccount = StringUtils.equals(stateChartCode, chartCode); 1682 isStateAccount &= StringUtils.equals(stateAccountNumber, accountNumber); 1683 isStateAccount &= StringUtils.equals(stateObjectCode, objectCode); 1684 1685 isTaxAccount = isFederalAccount || isStateAccount; 1686 } 1687 } 1688 1689 return isTaxAccount; 1690 } 1691 1692 public void setParameterService(ParameterService parameterService) { 1693 this.parameterService = parameterService; 1694 } 1695 1696 public void setPurApAccountingDao(PurApAccountingDao purApAccountingDao) { 1697 this.purApAccountingDao = purApAccountingDao; 1698 } 1699 1700 public void setPurapService(PurapService purapService) { 1701 this.purapService = purapService; 1702 } 1703 1704 @Override 1705 public List<SourceAccountingLine> mergeAccountingLineLists(List<SourceAccountingLine> accountingLines1, List<SourceAccountingLine> accountingLines2) { 1706 1707 KualiDecimal totalAmount = KualiDecimal.ZERO; 1708 List<SourceAccountingLine> mergedAccountList = new ArrayList(); 1709 1710 for (SourceAccountingLine line1 : accountingLines1) { 1711 KualiDecimal line1Amount = KualiDecimal.ZERO; 1712 if (ObjectUtils.isNotNull(line1.getAmount())) { 1713 line1Amount = line1.getAmount(); 1714 } 1715 1716 for (SourceAccountingLine line2 : accountingLines2) { 1717 KualiDecimal line2Amount = KualiDecimal.ZERO; 1718 if (ObjectUtils.isNotNull(line2.getAmount())) { 1719 line2Amount = line2.getAmount(); 1720 } 1721 1722 // if we find a match between lists, then merge amounts 1723 if (line1.equals(line2)) { 1724 // add the two amounts 1725 totalAmount = line1Amount.add(line2Amount); 1726 line1.setAmount(totalAmount); 1727 } 1728 } 1729 1730 mergedAccountList.add(line1); 1731 } 1732 1733 return mergedAccountList; 1734 } 1735 1736 /** 1737 * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyPaymentRequestIdentifier(java.lang.Integer) 1738 */ 1739 @Override 1740 public List getSummaryAccountsbyPaymentRequestIdentifier(Integer paymentRequestIdentifier) { 1741 if (paymentRequestIdentifier != null) { 1742 Map fieldValues = new HashMap(); 1743 fieldValues.put(PurapPropertyConstants.PAYMENT_REQUEST_ID, paymentRequestIdentifier); 1744 return new ArrayList(businessObjectService.findMatching(AccountsPayableSummaryAccount.class, fieldValues)); 1745 } 1746 return null; 1747 } 1748 1749 /** 1750 * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyInvoiceIdentifier(java.lang.Integer) 1751 */ 1752 @Override 1753 public List getSummaryAccountsbyInvoiceIdentifier(Integer invoiceIdentifier) { 1754 if (invoiceIdentifier != null) { 1755 Map fieldValues = new HashMap(); 1756 fieldValues.put(PurapPropertyConstants.INVOICE_ID, invoiceIdentifier); 1757 return new ArrayList(businessObjectService.findMatching(OleInvoiceAccountsPayableSummaryAccount.class, fieldValues)); 1758 } 1759 return null; 1760 } 1761 1762 /** 1763 * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyCreditMemoIdentifier(java.lang.Integer) 1764 */ 1765 @Override 1766 public List getSummaryAccountsbyCreditMemoIdentifier(Integer creditMemoIdentifier) { 1767 if (creditMemoIdentifier != null) { 1768 Map fieldValues = new HashMap(); 1769 fieldValues.put(PurapPropertyConstants.CREDIT_MEMO_ID, creditMemoIdentifier); 1770 return new ArrayList(businessObjectService.findMatching(AccountsPayableSummaryAccount.class, fieldValues)); 1771 } 1772 return null; 1773 } 1774 1775 /** 1776 * Sest the businessObjectService. 1777 * 1778 * @param businessObjectService 1779 */ 1780 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 1781 this.businessObjectService = businessObjectService; 1782 } 1783}