001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.module.purap.service.impl;
017:
018: import java.math.BigDecimal;
019: import java.util.ArrayList;
020: import java.util.HashSet;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Set;
024:
025: import org.kuali.core.util.KualiDecimal;
026: import org.kuali.core.util.ObjectUtils;
027: import org.kuali.kfs.bo.AccountingLineBase;
028: import org.kuali.kfs.bo.SourceAccountingLine;
029: import org.kuali.kfs.context.SpringContext;
030: import org.kuali.module.purap.bo.PurApAccountingLine;
031: import org.kuali.module.purap.bo.PurApItem;
032: import org.kuali.module.purap.bo.PurApSummaryItem;
033: import org.kuali.module.purap.dao.PurApAccountingDao;
034: import org.kuali.module.purap.document.PaymentRequestDocument;
035: import org.kuali.module.purap.document.PurchasingAccountsPayableDocument;
036: import org.kuali.module.purap.service.PurapAccountingService;
037: import org.kuali.module.purap.service.PurapService;
038: import org.kuali.module.purap.util.PurApItemUtils;
039: import org.kuali.module.purap.util.PurApObjectUtils;
040: import org.kuali.module.purap.util.SummaryAccount;
041: import org.springframework.transaction.annotation.Transactional;
042:
043: /**
044: *
045: * Contains a number of helper methods to deal with accounts on Purchasing Accounts Payable Documents
046: */
047: @Transactional
048: public class PurapAccountingServiceImpl implements
049: PurapAccountingService {
050: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
051: .getLogger(PurapAccountingServiceImpl.class);
052:
053: private static final BigDecimal ONE_HUNDRED = new BigDecimal(100);
054:
055: private static final int SCALE = 340;
056:
057: private static final int BIG_DECIMAL_ROUNDING_MODE = BigDecimal.ROUND_HALF_UP;
058:
059: // local constants
060: private static final Boolean ITEM_TYPES_INCLUDED_VALUE = Boolean.TRUE;;
061: private static final Boolean ITEM_TYPES_EXCLUDED_VALUE = Boolean.FALSE;
062: private static final Boolean ZERO_TOTALS_RETURNED_VALUE = Boolean.TRUE;
063: private static final Boolean ZERO_TOTALS_NOT_RETURNED_VALUE = Boolean.FALSE;
064: private static final Boolean ALTERNATE_AMOUNT_USED = Boolean.TRUE;
065: private static final Boolean ALTERNATE_AMOUNT_NOT_USED = Boolean.FALSE;
066:
067: // Spring injection
068: PurApAccountingDao purApAccountingDao;
069:
070: /**
071: *
072: * gets the lowest possible number for rounding, it works for ROUND_HALF_UP
073: * @return a BigDecimal representing the lowest possible number for rounding
074: */
075: private BigDecimal getLowestPossibleRoundUpNumber() {
076: BigDecimal startingDigit = new BigDecimal(0.5);
077: if (SCALE != 0) {
078: startingDigit = startingDigit.movePointLeft(SCALE);
079: }
080: return startingDigit;
081: }
082:
083: /**
084: *
085: * Helper method to log and throw an error
086: * @param methodName the method it's coming from
087: * @param errorMessage the actual error
088: */
089: private void throwRuntimeException(String methodName,
090: String errorMessage) {
091: LOG.error(methodName + " " + errorMessage);
092: throw new RuntimeException(errorMessage);
093: }
094:
095: /**
096: * @deprecated
097: * @see org.kuali.module.purap.service.PurapAccountingService#generateAccountDistributionForProration(java.util.List,
098: * org.kuali.core.util.KualiDecimal, java.lang.Integer)
099: */
100: public List<PurApAccountingLine> generateAccountDistributionForProration(
101: List<SourceAccountingLine> accounts,
102: KualiDecimal totalAmount, Integer percentScale) {
103: return null;
104: }
105:
106: /**
107: * @see org.kuali.module.purap.service.PurapAccountingService#generateAccountDistributionForProration(java.util.List,
108: * org.kuali.core.util.KualiDecimal, java.lang.Integer)
109: */
110: public List<PurApAccountingLine> generateAccountDistributionForProration(
111: List<SourceAccountingLine> accounts,
112: KualiDecimal totalAmount, Integer percentScale, Class clazz) {
113: String methodName = "generateAccountDistributionForProration()";
114: LOG.debug(methodName + " started");
115: List<PurApAccountingLine> newAccounts = new ArrayList();
116:
117: if (totalAmount.isZero()) {
118: throwRuntimeException(
119: methodName,
120: "Purchasing/Accounts Payable account distribution for proration does not allow zero dollar total.");
121: }
122:
123: BigDecimal percentTotal = BigDecimal.ZERO;
124: BigDecimal totalAmountBigDecimal = totalAmount
125: .bigDecimalValue();
126: for (SourceAccountingLine accountingLine : accounts) {
127: LOG.debug(methodName + " "
128: + accountingLine.getAccountNumber() + " "
129: + accountingLine.getAmount() + "/"
130: + totalAmountBigDecimal);
131: BigDecimal pct = accountingLine.getAmount()
132: .bigDecimalValue().divide(totalAmountBigDecimal,
133: percentScale, BIG_DECIMAL_ROUNDING_MODE);
134: pct = pct.multiply(ONE_HUNDRED).stripTrailingZeros();
135:
136: LOG.debug(methodName + " pct = " + pct
137: + " (trailing zeros removed)");
138:
139: BigDecimal lowestPossible = this
140: .getLowestPossibleRoundUpNumber();
141: if (lowestPossible.compareTo(pct) <= 0) {
142: PurApAccountingLine newAccountingLine;
143: newAccountingLine = null;
144:
145: try {
146: newAccountingLine = (PurApAccountingLine) clazz
147: .newInstance();
148: } catch (InstantiationException e) {
149: e.printStackTrace();
150: } catch (IllegalAccessException e) {
151: e.printStackTrace();
152: }
153:
154: PurApObjectUtils.populateFromBaseClass(
155: AccountingLineBase.class, accountingLine,
156: newAccountingLine);
157: newAccountingLine.setAccountLinePercent(pct);
158: LOG.debug(methodName + " adding "
159: + newAccountingLine.getAccountLinePercent());
160: newAccounts.add(newAccountingLine);
161: percentTotal = percentTotal.add(newAccountingLine
162: .getAccountLinePercent());
163: LOG.debug(methodName + " total = " + percentTotal);
164: }
165: }
166:
167: if ((percentTotal.compareTo(BigDecimal.ZERO)) == 0) {
168: /*
169: * This means there are so many accounts or so strange a distribution that we can't round properly... not sure of viable
170: * solution
171: */
172: throwRuntimeException(methodName,
173: "Can't round properly due to number of accounts");
174: }
175:
176: // Now deal with rounding
177: if ((ONE_HUNDRED.compareTo(percentTotal)) < 0) {
178: /*
179: * The total percent is greater than one hundred Here we find the account that occurs latest in our list with a percent
180: * that is higher than the difference and we subtract off the difference
181: */
182: BigDecimal difference = percentTotal.subtract(ONE_HUNDRED);
183: LOG.debug(methodName + " Rounding up by " + difference);
184:
185: boolean foundAccountToUse = false;
186: int currentNbr = newAccounts.size() - 1;
187: while (currentNbr >= 0) {
188: PurApAccountingLine potentialSlushAccount = (PurApAccountingLine) newAccounts
189: .get(currentNbr);
190: if ((difference.compareTo(potentialSlushAccount
191: .getAccountLinePercent())) < 0) {
192: // the difference amount is less than the current accounts percent... use this account
193: // the 'potentialSlushAccount' technically is now the true 'Slush Account'
194: potentialSlushAccount
195: .setAccountLinePercent((potentialSlushAccount
196: .getAccountLinePercent()
197: .subtract(difference))
198: .stripTrailingZeros());
199: foundAccountToUse = true;
200: break;
201: }
202: currentNbr--;
203: }
204:
205: if (!foundAccountToUse) {
206: /*
207: * We could not find any account in our list where the percent of that account was greater than that of the
208: * difference... doing so on just any account could result in a negative percent value
209: */
210: throwRuntimeException(methodName,
211: "Can't round properly due to math calculation error");
212: }
213:
214: } else if ((ONE_HUNDRED.compareTo(percentTotal)) > 0) {
215: /*
216: * The total percent is less than one hundred Here we find the last account in our list and add the remaining required
217: * percent to it's already calculated percent
218: */
219: BigDecimal difference = ONE_HUNDRED.subtract(percentTotal);
220: LOG.debug(methodName + " Rounding down by " + difference);
221: PurApAccountingLine slushAccount = (PurApAccountingLine) newAccounts
222: .get(newAccounts.size() - 1);
223: slushAccount.setAccountLinePercent((slushAccount
224: .getAccountLinePercent().add(difference))
225: .stripTrailingZeros());
226: }
227: LOG.debug(methodName + " ended");
228: return newAccounts;
229: }
230:
231: /**
232: * @see org.kuali.module.purap.service.PurapAccountingService#generateAccountDistributionForProrationWithZeroTotal(java.util.List,
233: * java.lang.Integer)
234: */
235: public List<PurApAccountingLine> generateAccountDistributionForProrationWithZeroTotal(
236: List<PurApAccountingLine> accounts, Integer percentScale) {
237: String methodName = "generateAccountDistributionForProrationWithZeroTotal()";
238: LOG.debug(methodName + " started");
239:
240: // find the total percent and strip trailing zeros
241: BigDecimal totalPercentValue = BigDecimal.ZERO;
242: for (PurApAccountingLine accountingLine : accounts) {
243: totalPercentValue = (totalPercentValue.add(accountingLine
244: .getAccountLinePercent())).stripTrailingZeros();
245: }
246:
247: if ((BigDecimal.ZERO.compareTo(totalPercentValue
248: .remainder(ONE_HUNDRED))) != 0) {
249: throwRuntimeException(
250: methodName,
251: "Invalid Percent Total of '"
252: + totalPercentValue
253: + "' does not allow for account distribution (must be multiple of 100)");
254: }
255:
256: List newAccounts = new ArrayList();
257: BigDecimal logDisplayOnlyTotal = BigDecimal.ZERO;
258: BigDecimal percentUsed = BigDecimal.ZERO;
259: int accountListSize = accounts.size();
260: int i = 0;
261: for (PurApAccountingLine accountingLine : accounts) {
262: i++;
263: BigDecimal percentToUse = BigDecimal.ZERO;
264: LOG.debug(methodName + " "
265: + accountingLine.getChartOfAccountsCode() + "-"
266: + accountingLine.getAccountNumber() + " "
267: + accountingLine.getAmount() + "/" + percentToUse);
268:
269: // if it's the last account make up the leftover percent
270: BigDecimal acctPercent = accountingLine
271: .getAccountLinePercent();
272: if ((i != accountListSize) || (accountListSize == 1)) {
273: // this account is not the last account or there is only one account
274: percentToUse = (acctPercent.divide(totalPercentValue,
275: SCALE, BIG_DECIMAL_ROUNDING_MODE))
276: .multiply(ONE_HUNDRED);
277: percentUsed = percentUsed.add(((acctPercent.divide(
278: totalPercentValue, SCALE,
279: BIG_DECIMAL_ROUNDING_MODE)))
280: .multiply(ONE_HUNDRED));
281: } else {
282: // this account is the last account so we have to makeup whatever is left out of 100
283: percentToUse = ONE_HUNDRED.subtract(percentUsed);
284: }
285:
286: PurApAccountingLine newAccountingLine = accountingLine
287: .createBlankAmountsCopy();
288: LOG.debug(methodName + " pct = " + percentToUse);
289: newAccountingLine.setAccountLinePercent(percentToUse
290: .setScale(accountingLine.getAccountLinePercent()
291: .scale(), BIG_DECIMAL_ROUNDING_MODE));
292: LOG.debug(methodName + " adding "
293: + newAccountingLine.getAccountLinePercent());
294: newAccounts.add(newAccountingLine);
295: logDisplayOnlyTotal = logDisplayOnlyTotal
296: .add(newAccountingLine.getAccountLinePercent());
297: LOG.debug(methodName + " total = " + logDisplayOnlyTotal);
298: }
299: LOG.debug(methodName + " ended");
300: return newAccounts;
301: }
302:
303: /**
304: * @see org.kuali.module.purap.service.PurapAccountingService#generateSummary(java.util.List)
305: */
306: public List<SourceAccountingLine> generateSummary(
307: List<PurApItem> items) {
308: String methodName = "generateSummary()";
309: LOG.debug(methodName + " started");
310: List<SourceAccountingLine> returnList = generateAccountSummary(
311: items, null, ITEM_TYPES_EXCLUDED_VALUE,
312: ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED);
313: LOG.debug(methodName + " ended");
314: return returnList;
315: }
316:
317: /**
318: *
319: * @see org.kuali.module.purap.service.PurapAccountingService#generateSummaryAccounts(org.kuali.module.purap.document.PurchasingAccountsPayableDocument)
320: */
321: public List<SummaryAccount> generateSummaryAccounts(
322: PurchasingAccountsPayableDocument document) {
323: // always update the amounts first
324: updateAccountAmounts(document);
325: return generateSummaryAccounts(document.getItems());
326: }
327:
328: /**
329: *
330: * This creates summary accounts based on a list of items.
331: * @param items a list of PurAp Items.
332: * @return a list of summary accounts.
333: */
334: private List<SummaryAccount> generateSummaryAccounts(
335: List<PurApItem> items) {
336: String methodName = "generateSummaryAccounts()";
337: List<SummaryAccount> returnList = new ArrayList<SummaryAccount>();
338: LOG.debug(methodName + " started");
339: List<SourceAccountingLine> sourceLines = generateSummary(items);
340: for (SourceAccountingLine sourceAccountingLine : sourceLines) {
341: SummaryAccount summaryAccount = new SummaryAccount();
342: summaryAccount
343: .setAccount((SourceAccountingLine) ObjectUtils
344: .deepCopy(sourceAccountingLine));
345: for (PurApItem item : items) {
346: List<PurApAccountingLine> itemAccounts = item
347: .getSourceAccountingLines();
348: for (PurApAccountingLine purApAccountingLine : itemAccounts) {
349: if (purApAccountingLine
350: .accountStringsAreEqual(summaryAccount
351: .getAccount())) {
352: PurApSummaryItem summaryItem = item
353: .getSummaryItem();
354: summaryItem
355: .setEstimatedEncumberanceAmount(purApAccountingLine
356: .getAmount());
357: summaryAccount.getItems().add(summaryItem);
358: break;
359: }
360: }
361: }
362: returnList.add(summaryAccount);
363: }
364: LOG.debug(methodName + " ended");
365: return returnList;
366: }
367:
368: /**
369: * @see org.kuali.module.purap.service.PurapAccountingService#generateSummaryWithNoZeroTotals(java.util.List)
370: */
371: public List<SourceAccountingLine> generateSummaryWithNoZeroTotals(
372: List<PurApItem> items) {
373: String methodName = "generateSummaryWithNoZeroTotals()";
374: LOG.debug(methodName + " started");
375: List<SourceAccountingLine> returnList = generateAccountSummary(
376: items, null, ITEM_TYPES_EXCLUDED_VALUE,
377: ZERO_TOTALS_NOT_RETURNED_VALUE,
378: ALTERNATE_AMOUNT_NOT_USED);
379: LOG.debug(methodName + " ended");
380: return returnList;
381: }
382:
383: /**
384: * @see org.kuali.module.purap.service.PurapAccountingService#generateSummaryWithNoZeroTotalsUsingAlternateAmount(java.util.List)
385: */
386: public List<SourceAccountingLine> generateSummaryWithNoZeroTotalsUsingAlternateAmount(
387: List<PurApItem> items) {
388: String methodName = "generateSummaryWithNoZeroTotals()";
389: LOG.debug(methodName + " started");
390: List<SourceAccountingLine> returnList = generateAccountSummary(
391: items, null, ITEM_TYPES_EXCLUDED_VALUE,
392: ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_USED);
393: LOG.debug(methodName + " ended");
394: return returnList;
395: }
396:
397: /**
398: * @see org.kuali.module.purap.service.PurapAccountingService#generateSummaryExcludeItemTypes(java.util.List, java.util.Set)
399: */
400: public List<SourceAccountingLine> generateSummaryExcludeItemTypes(
401: List<PurApItem> items, Set excludedItemTypeCodes) {
402: String methodName = "generateSummaryExcludeItemTypes()";
403: LOG.debug(methodName + " started");
404: List<SourceAccountingLine> returnList = generateAccountSummary(
405: items, excludedItemTypeCodes,
406: ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE,
407: ALTERNATE_AMOUNT_NOT_USED);
408: LOG.debug(methodName + " ended");
409: return returnList;
410: }
411:
412: /**
413: * @see org.kuali.module.purap.service.PurapAccountingService#generateSummaryIncludeItemTypesAndNoZeroTotals(java.util.List,
414: * java.util.Set)
415: */
416: public List<SourceAccountingLine> generateSummaryIncludeItemTypesAndNoZeroTotals(
417: List<PurApItem> items, Set includedItemTypeCodes) {
418: String methodName = "generateSummaryExcludeItemTypesAndNoZeroTotals()";
419: LOG.debug(methodName + " started");
420: List<SourceAccountingLine> returnList = generateAccountSummary(
421: items, includedItemTypeCodes,
422: ITEM_TYPES_INCLUDED_VALUE,
423: ZERO_TOTALS_NOT_RETURNED_VALUE,
424: ALTERNATE_AMOUNT_NOT_USED);
425: LOG.debug(methodName + " ended");
426: return returnList;
427: }
428:
429: /**
430: * @see org.kuali.module.purap.service.PurapAccountingService#generateSummaryIncludeItemTypes(java.util.List, java.util.Set)
431: */
432: public List<SourceAccountingLine> generateSummaryIncludeItemTypes(
433: List<PurApItem> items, Set includedItemTypeCodes) {
434: String methodName = "generateSummaryIncludeItemTypes()";
435: LOG.debug(methodName + " started");
436: List<SourceAccountingLine> returnList = generateAccountSummary(
437: items, includedItemTypeCodes,
438: ITEM_TYPES_INCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE,
439: ALTERNATE_AMOUNT_NOT_USED);
440: LOG.debug(methodName + " ended");
441: return returnList;
442: }
443:
444: /**
445: * @see org.kuali.module.purap.service.PurapAccountingService#generateSummaryExcludeItemTypesAndNoZeroTotals(java.util.List,
446: * java.util.Set)
447: */
448: public List<SourceAccountingLine> generateSummaryExcludeItemTypesAndNoZeroTotals(
449: List<PurApItem> items, Set excludedItemTypeCodes) {
450: String methodName = "generateSummaryIncludeItemTypesAndNoZeroTotals()";
451: LOG.debug(methodName + " started");
452: List<SourceAccountingLine> returnList = generateAccountSummary(
453: items, excludedItemTypeCodes,
454: ITEM_TYPES_EXCLUDED_VALUE,
455: ZERO_TOTALS_NOT_RETURNED_VALUE,
456: ALTERNATE_AMOUNT_NOT_USED);
457: LOG.debug(methodName + " ended");
458: return returnList;
459: }
460:
461: /**
462: *
463: * Generates an account summary, that is it creates a list of source accounts
464: * by rounding up the purap accounts off of the purap items.
465: * @param items the items to determ
466: * @param itemTypeCodes the item types to determine whether to look at an item in combination with itemTypeCodesAreIncluded
467: * @param itemTypeCodesAreIncluded value to tell whether the itemTypeCodes parameter lists inclusion or exclusion variables
468: * @param useZeroTotals whether to include items with a zero dollar total
469: * @param useAlternateAmount an alternate amount used in certain cases for GL entry
470: * @return a list of source accounts
471: */
472: private List<SourceAccountingLine> generateAccountSummary(
473: List<PurApItem> items, Set itemTypeCodes,
474: Boolean itemTypeCodesAreIncluded, Boolean useZeroTotals,
475: Boolean useAlternateAmount) {
476: List<PurApItem> itemsToProcess = getProcessablePurapItems(
477: items, itemTypeCodes, itemTypeCodesAreIncluded,
478: useZeroTotals);
479: Set<PurApAccountingLine> accountSet = new HashSet<PurApAccountingLine>();
480:
481: for (PurApItem currentItem : items) {
482: if (PurApItemUtils.checkItemActive(currentItem)) {
483: for (PurApAccountingLine account : currentItem
484: .getSourceAccountingLines()) {
485: boolean this AccountAlreadyInSet = false;
486: for (Iterator iter = accountSet.iterator(); iter
487: .hasNext();) {
488: PurApAccountingLine alreadyAddedAccount = (PurApAccountingLine) iter
489: .next();
490: if (alreadyAddedAccount
491: .accountStringsAreEqual(account)) {
492: if (useAlternateAmount) {
493: alreadyAddedAccount
494: .setAlternateAmountForGLEntryCreation(alreadyAddedAccount
495: .getAlternateAmountForGLEntryCreation()
496: .add(
497: account
498: .getAlternateAmountForGLEntryCreation()));
499: } else {
500: alreadyAddedAccount
501: .setAmount(alreadyAddedAccount
502: .getAmount()
503: .add(
504: account
505: .getAmount()));
506: }
507: this AccountAlreadyInSet = true;
508: break;
509: }
510: }
511: if (!this AccountAlreadyInSet) {
512: PurApAccountingLine accountToAdd = (PurApAccountingLine) ObjectUtils
513: .deepCopy(account);
514: accountSet.add(accountToAdd);
515: }
516: }
517: }
518: }
519:
520: // convert list of PurApAccountingLine objects to SourceAccountingLineObjects
521: List<SourceAccountingLine> sourceAccounts = new ArrayList<SourceAccountingLine>();
522: for (Iterator iter = accountSet.iterator(); iter.hasNext();) {
523: PurApAccountingLine accountToAlter = (PurApAccountingLine) iter
524: .next();
525: if (accountToAlter.isEmpty()) {
526: String errorMessage = "Found an 'empty' account in summary generation "
527: + accountToAlter.toString();
528: LOG.error("generateAccountSummary() " + errorMessage);
529: throw new RuntimeException(errorMessage);
530: }
531: SourceAccountingLine sourceLine = accountToAlter
532: .generateSourceAccountingLine();
533: if (useAlternateAmount) {
534: sourceLine.setAmount(accountToAlter
535: .getAlternateAmountForGLEntryCreation());
536: }
537: sourceAccounts.add(sourceLine);
538: }
539: return sourceAccounts;
540: }
541:
542: /**
543: * This method takes a list of {@link PurchasingApItem} objects and parses through them to see if each one should be processed
544: * according the the other variables passed in.<br>
545: * <br>
546: * Example 1:<br>
547: * items = "ITEM", "SITM", "FRHT", "SPHD"<br>
548: * itemTypeCodes = "FRHT"<br>
549: * itemTypeCodesAreIncluded = ITEM_TYPES_EXCLUDED_VALUE<br>
550: * return items "ITEM", "SITM", "FRHT", "SPHD"<br>
551: * <br>
552: * <br>
553: * Example 2:<br>
554: * items = "ITEM", "SITM", "FRHT", "SPHD"<br>
555: * itemTypeCodes = "ITEM","FRHT"<br>
556: * itemTypeCodesAreIncluded = ITEM_TYPES_INCLUDED_VALUE<br>
557: * return items "ITEM", "FRHT"<br>
558: *
559: * @param items - list of {@link PurchasingApItem} objects that need to be parsed
560: * @param itemTypeCodes - list of {@link org.kuali.module.purap.bo.ItemType} codes used in conjunction with
561: * itemTypeCodesAreIncluded parameter
562: * @param itemTypeCodesAreIncluded - value to tell whether the itemTypeCodes parameter lists inclusion or exclusion variables
563: * (see {@link #ITEM_TYPES_INCLUDED_VALUE})
564: * @param useZeroTotals - value to tell whether to include zero dollar items (see {@link #ZERO_TOTALS_RETURNED_VALUE})
565: * @return a list of {@link PurchasingApItem} objects that should be used for processing by calling method
566: */
567: private List<PurApItem> getProcessablePurapItems(
568: List<PurApItem> items, Set itemTypeCodes,
569: Boolean itemTypeCodesAreIncluded, Boolean useZeroTotals) {
570: String methodName = "getProcessablePurapItems()";
571: List<PurApItem> newItemList = new ArrayList<PurApItem>();
572: // error out if we have an invalid 'itemTypeCodesAreIncluded' value
573: if ((!(ITEM_TYPES_INCLUDED_VALUE
574: .equals(itemTypeCodesAreIncluded)))
575: && (!(ITEM_TYPES_EXCLUDED_VALUE
576: .equals(itemTypeCodesAreIncluded)))) {
577: throwRuntimeException(
578: methodName,
579: "Invalid parameter found while trying to find processable items for dealing with purchasing/accounts payable accounts");
580: }
581: for (PurApItem currentItem : items) {
582: if ((itemTypeCodes != null) && (!(itemTypeCodes.isEmpty()))) {
583: // we have at least one entry in our item type code list
584: boolean foundMatchInList = false;
585: // check to see if this item type code is in the list
586: for (Iterator iterator = itemTypeCodes.iterator(); iterator
587: .hasNext();) {
588: String itemTypeCode = (String) iterator.next();
589: // include this item if it's in the included list
590: if (itemTypeCode.equals(currentItem.getItemType()
591: .getItemTypeCode())) {
592: foundMatchInList = true;
593: break;
594: }
595: }
596: // check to see if item type code was found and if the list is describing included or excluded item types
597: if ((foundMatchInList)
598: && (ITEM_TYPES_EXCLUDED_VALUE
599: .equals(itemTypeCodesAreIncluded))) {
600: // this item type code is in the list
601: // this item type code is excluded so we skip it
602: continue; // skips current item
603: } else if ((!foundMatchInList)
604: && (ITEM_TYPES_INCLUDED_VALUE
605: .equals(itemTypeCodesAreIncluded))) {
606: // this item type code is not in the list
607: // this item type code is not included so we skip it
608: continue; // skips current item
609: }
610: } else {
611: // the item type code list is empty
612: if (ITEM_TYPES_INCLUDED_VALUE
613: .equals(itemTypeCodesAreIncluded)) {
614: // the item type code list is empty and the list is supposed to contain the item types to include
615: throwRuntimeException(
616: methodName,
617: "Invalid parameter and list of items found while trying to find processable items for dealing with purchasing/accounts payable accounts");
618: }
619: }
620: if ((ZERO_TOTALS_NOT_RETURNED_VALUE.equals(useZeroTotals))
621: && (ObjectUtils.isNull(currentItem
622: .getExtendedPrice()) || ((KualiDecimal.ZERO
623: .compareTo(currentItem.getExtendedPrice())) == 0))) {
624: // if we don't return zero dollar items then skip this one
625: continue;
626: }
627: newItemList.add(currentItem);
628: }
629: return newItemList;
630: }
631:
632: /**
633: *
634: * @see org.kuali.module.purap.service.PurapAccountingService#updateAccountAmounts(org.kuali.module.purap.document.PurchasingAccountsPayableDocument)
635: */
636: public void updateAccountAmounts(
637: PurchasingAccountsPayableDocument document) {
638: // the percent at fiscal approve
639: // don't update if past the AP review level
640: if ((document instanceof PaymentRequestDocument)
641: && SpringContext.getBean(PurapService.class)
642: .isFullDocumentEntryCompleted(document)) {
643: return;
644: }
645: for (PurApItem item : document.getItems()) {
646: updateItemAccountAmounts(item);
647: }
648:
649: }
650:
651: /**
652: *
653: * @see org.kuali.module.purap.service.PurapAccountingService#updateItemAccountAmounts(org.kuali.module.purap.bo.PurApItem)
654: */
655: public void updateItemAccountAmounts(PurApItem item) {
656: if ((item.getExtendedPrice() != null)
657: && KualiDecimal.ZERO.compareTo(item.getExtendedPrice()) != 0) {
658:
659: KualiDecimal accountTotal = KualiDecimal.ZERO;
660: PurApAccountingLine lastAccount = null;
661:
662: for (PurApAccountingLine account : item
663: .getSourceAccountingLines()) {
664: BigDecimal pct = new BigDecimal(account
665: .getAccountLinePercent().toString())
666: .divide(new BigDecimal(100));
667: account.setAmount(new KualiDecimal(pct
668: .multiply(new BigDecimal(item
669: .getExtendedPrice().toString()))));
670: accountTotal = accountTotal.add(account.getAmount());
671: lastAccount = account;
672: }
673:
674: // put excess on last account
675: if (lastAccount != null) {
676: KualiDecimal difference = item.getExtendedPrice()
677: .subtract(accountTotal);
678: lastAccount.setAmount(lastAccount.getAmount().add(
679: difference));
680: }
681: } else {
682: // zero out if extended price is zero
683: for (PurApAccountingLine account : item
684: .getSourceAccountingLines()) {
685: account.setAmount(KualiDecimal.ZERO);
686: }
687: }
688: }
689:
690: public List<PurApAccountingLine> getAccountsFromItem(PurApItem item) {
691: return purApAccountingDao.getAccountingLinesForItem(item);
692: }
693:
694: public PurApAccountingDao getPurApAccountingDao() {
695: return purApAccountingDao;
696: }
697:
698: public void setPurApAccountingDao(
699: PurApAccountingDao purApAccountingDao) {
700: this.purApAccountingDao = purApAccountingDao;
701: }
702:
703: }
|