org.kuali.ole.module.purap.service.impl.PurapAccountingServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.ole.module.purap.service.impl.PurapAccountingServiceImpl.java

Source

/*
 * Copyright 2007 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.ole.module.purap.service.impl;

import org.apache.commons.lang.StringUtils;
import org.kuali.ole.module.purap.PurapConstants;
import org.kuali.ole.module.purap.PurapConstants.PurapDocTypeCodes;
import org.kuali.ole.module.purap.PurapKeyConstants;
import org.kuali.ole.module.purap.PurapParameterConstants.NRATaxParameters;
import org.kuali.ole.module.purap.PurapPropertyConstants;
import org.kuali.ole.module.purap.businessobject.*;
import org.kuali.ole.module.purap.dataaccess.PurApAccountingDao;
import org.kuali.ole.module.purap.document.InvoiceDocument;
import org.kuali.ole.module.purap.document.PaymentRequestDocument;
import org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument;
import org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocumentBase;
import org.kuali.ole.module.purap.document.service.PurapService;
import org.kuali.ole.module.purap.service.PurapAccountingService;
import org.kuali.ole.module.purap.util.PurApItemUtils;
import org.kuali.ole.module.purap.util.PurApObjectUtils;
import org.kuali.ole.module.purap.util.SummaryAccount;
import org.kuali.ole.module.purap.util.UseTaxContainer;
import org.kuali.ole.select.businessobject.*;
import org.kuali.ole.select.document.OleInvoiceDocument;
import org.kuali.ole.select.document.OlePaymentRequestDocument;
import org.kuali.ole.select.document.OleVendorCreditMemoDocument;
import org.kuali.ole.sys.businessobject.AccountingLineBase;
import org.kuali.ole.sys.businessobject.SourceAccountingLine;
import org.kuali.ole.sys.context.SpringContext;
import org.kuali.ole.sys.service.NonTransactional;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.KualiRuleService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * Contains a number of helper methods to deal with accounts on Purchasing Accounts Payable Documents
 */

@NonTransactional
public class PurapAccountingServiceImpl implements PurapAccountingService {
    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
            .getLogger(PurapAccountingServiceImpl.class);

    protected static final BigDecimal ONE_HUNDRED = new BigDecimal(100);
    protected static final int SCALE = 340;
    protected static final int BIG_DECIMAL_ROUNDING_MODE = BigDecimal.ROUND_HALF_UP;
    protected static final int BIG_DECIMAL_SCALE = 2;

    // local constants
    protected static final Boolean ITEM_TYPES_INCLUDED_VALUE = Boolean.TRUE;;
    protected static final Boolean ITEM_TYPES_EXCLUDED_VALUE = Boolean.FALSE;
    protected static final Boolean ZERO_TOTALS_RETURNED_VALUE = Boolean.TRUE;
    protected static final Boolean ZERO_TOTALS_NOT_RETURNED_VALUE = Boolean.FALSE;
    protected static final Boolean ALTERNATE_AMOUNT_USED = Boolean.TRUE;
    protected static final Boolean ALTERNATE_AMOUNT_NOT_USED = Boolean.FALSE;
    protected static final Boolean USE_TAX_INCLUDED = Boolean.TRUE;
    protected static final Boolean USE_TAX_EXCLUDED = Boolean.FALSE;

    protected ParameterService parameterService;
    protected PurapService purapService;
    protected PurApAccountingDao purApAccountingDao;
    protected BusinessObjectService businessObjectService;
    private OlePaymentRequestDocument olePaymentRequestDocument;

    /**
     * gets the lowest possible number for rounding, it works for ROUND_HALF_UP
     *
     * @return a BigDecimal representing the lowest possible number for rounding
     */
    protected BigDecimal getLowestPossibleRoundUpNumber() {
        BigDecimal startingDigit = new BigDecimal(0.5);
        if (SCALE != 0) {
            startingDigit = startingDigit.movePointLeft(SCALE);
        }
        return startingDigit;
    }

    /**
     * Helper method to log and throw an error
     *
     * @param methodName   the method it's coming from
     * @param errorMessage the actual error
     */
    protected void throwRuntimeException(String methodName, String errorMessage) {
        LOG.error(methodName + "  " + errorMessage);
        throw new RuntimeException(errorMessage);
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateAccountDistributionForProration(java.util.List,
     *      org.kuali.rice.kns.util.KualiDecimal, java.lang.Integer)
     * @deprecated
     */
    @Deprecated
    @Override
    public List<PurApAccountingLine> generateAccountDistributionForProration(List<SourceAccountingLine> accounts,
            KualiDecimal totalAmount, Integer percentScale) {
        return null;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateAccountDistributionForProration(java.util.List,
     *      org.kuali.rice.kns.util.KualiDecimal, java.lang.Integer)
     */
    @Override
    public List<PurApAccountingLine> generateAccountDistributionForProration(List<SourceAccountingLine> accounts,
            KualiDecimal totalAmount, Integer percentScale, Class clazz) {
        String methodName = "generateAccountDistributionForProration()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<PurApAccountingLine> newAccounts = new ArrayList();

        if (totalAmount.isZero()) {
            throwRuntimeException(methodName,
                    "Purchasing/Accounts Payable account distribution for proration does not allow zero dollar total.");
        }

        BigDecimal percentTotal = BigDecimal.ZERO;
        BigDecimal totalAmountBigDecimal = totalAmount.bigDecimalValue();
        for (SourceAccountingLine accountingLine : accounts) {
            KualiDecimal amt = KualiDecimal.ZERO;
            if (ObjectUtils.isNotNull(accountingLine.getAmount())) {
                amt = accountingLine.getAmount();
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug(methodName + " " + accountingLine.getAccountNumber() + " " + amt + "/"
                        + totalAmountBigDecimal);
            }
            BigDecimal pct = amt.bigDecimalValue().divide(totalAmountBigDecimal, percentScale,
                    BIG_DECIMAL_ROUNDING_MODE);
            pct = pct.stripTrailingZeros().multiply(ONE_HUNDRED);

            if (LOG.isDebugEnabled()) {
                LOG.debug(methodName + " pct = " + pct + "  (trailing zeros removed)");
            }

            BigDecimal lowestPossible = this.getLowestPossibleRoundUpNumber();
            if (lowestPossible.compareTo(pct) <= 0) {
                PurApAccountingLine newAccountingLine;
                newAccountingLine = null;

                try {
                    newAccountingLine = (PurApAccountingLine) clazz.newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

                PurApObjectUtils.populateFromBaseClass(AccountingLineBase.class, accountingLine, newAccountingLine);
                newAccountingLine.setAccountLinePercent(pct);
                if (LOG.isDebugEnabled()) {
                    LOG.debug(methodName + " adding " + newAccountingLine.getAccountLinePercent());
                }
                newAccounts.add(newAccountingLine);
                percentTotal = percentTotal.add(newAccountingLine.getAccountLinePercent());
                if (LOG.isDebugEnabled()) {
                    LOG.debug(methodName + " total = " + percentTotal);
                }
            }
        }

        if ((percentTotal.compareTo(BigDecimal.ZERO)) == 0) {
            /*
             * This means there are so many accounts or so strange a distribution that we can't round properly... not sure of viable
             * solution
             */
            throwRuntimeException(methodName, "Can't round properly due to number of accounts");
        }

        // Now deal with rounding
        if ((ONE_HUNDRED.compareTo(percentTotal)) < 0) {
            /*
             * The total percent is greater than one hundred Here we find the account that occurs latest in our list with a percent
             * that is higher than the difference and we subtract off the difference
             */
            BigDecimal difference = percentTotal.subtract(ONE_HUNDRED);
            if (LOG.isDebugEnabled()) {
                LOG.debug(methodName + " Rounding up by " + difference);
            }

            boolean foundAccountToUse = false;
            int currentNbr = newAccounts.size() - 1;
            while (currentNbr >= 0) {
                PurApAccountingLine potentialSlushAccount = newAccounts.get(currentNbr);

                BigDecimal linePercent = BigDecimal.ZERO;
                if (ObjectUtils.isNotNull(potentialSlushAccount.getAccountLinePercent())) {
                    linePercent = potentialSlushAccount.getAccountLinePercent();
                }

                if ((difference.compareTo(linePercent)) < 0) {
                    // the difference amount is less than the current accounts percent... use this account
                    // the 'potentialSlushAccount' technically is now the true 'Slush Account'
                    potentialSlushAccount.setAccountLinePercent(linePercent.subtract(difference).movePointLeft(2)
                            .stripTrailingZeros().movePointRight(2));
                    foundAccountToUse = true;
                    break;
                }
                currentNbr--;
            }

            if (!foundAccountToUse) {
                /*
                 * We could not find any account in our list where the percent of that account was greater than that of the
                 * difference... doing so on just any account could result in a negative percent value
                 */
                throwRuntimeException(methodName, "Can't round properly due to math calculation error");
            }

        } else if ((ONE_HUNDRED.compareTo(percentTotal)) > 0) {
            /*
             * The total percent is less than one hundred Here we find the last account in our list and add the remaining required
             * percent to its already calculated percent
             */
            BigDecimal difference = ONE_HUNDRED.subtract(percentTotal);
            if (LOG.isDebugEnabled()) {
                LOG.debug(methodName + " Rounding down by " + difference);
            }
            PurApAccountingLine slushAccount = newAccounts.get(newAccounts.size() - 1);

            BigDecimal slushLinePercent = BigDecimal.ZERO;
            if (ObjectUtils.isNotNull(slushAccount.getAccountLinePercent())) {
                slushLinePercent = slushAccount.getAccountLinePercent();
            }

            slushAccount.setAccountLinePercent(
                    slushLinePercent.add(difference).movePointLeft(2).stripTrailingZeros().movePointRight(2));
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return newAccounts;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateAccountDistributionForProrationWithZeroTotal(java.util.List,
     *      java.lang.Integer)
     */
    @Override
    public List<PurApAccountingLine> generateAccountDistributionForProrationWithZeroTotal(
            PurchasingAccountsPayableDocument purapDoc) {
        String methodName = "generateAccountDistributionForProrationWithZeroTotal()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }

        List<PurApAccountingLine> accounts = generatePercentSummary(purapDoc);

        // find the total percent and strip trailing zeros
        BigDecimal totalPercentValue = BigDecimal.ZERO;
        for (PurApAccountingLine accountingLine : accounts) {
            BigDecimal linePercent = BigDecimal.ZERO;
            if (ObjectUtils.isNotNull(accountingLine.getAccountLinePercent())) {
                linePercent = accountingLine.getAccountLinePercent();
            }

            totalPercentValue = totalPercentValue.add(linePercent).movePointLeft(2).stripTrailingZeros()
                    .movePointRight(2);
        }

        if ((BigDecimal.ZERO.compareTo(totalPercentValue.remainder(ONE_HUNDRED))) != 0) {
            throwRuntimeException(methodName, "Invalid Percent Total of '" + totalPercentValue
                    + "' does not allow for account distribution (must be multiple of 100)");
        }

        List newAccounts = new ArrayList();
        BigDecimal logDisplayOnlyTotal = BigDecimal.ZERO;
        BigDecimal percentUsed = BigDecimal.ZERO;
        int accountListSize = accounts.size();
        int i = 0;
        for (PurApAccountingLine accountingLine : accounts) {
            i++;
            BigDecimal percentToUse = BigDecimal.ZERO;
            KualiDecimal amt = KualiDecimal.ZERO;

            if (ObjectUtils.isNotNull(accountingLine.getAmount())) {
                amt = accountingLine.getAmount();
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug(methodName + " " + accountingLine.getChartOfAccountsCode() + "-"
                        + accountingLine.getAccountNumber() + " " + amt + "/" + percentToUse);
            }

            // if it's the last account make up the leftover percent
            BigDecimal acctPercent = BigDecimal.ZERO;
            if (ObjectUtils.isNotNull(accountingLine.getAccountLinePercent())) {
                acctPercent = accountingLine.getAccountLinePercent();
            }

            if ((i != accountListSize) || (accountListSize == 1)) {
                // this account is not the last account or there is only one account
                percentToUse = (acctPercent.divide(totalPercentValue, SCALE, BIG_DECIMAL_ROUNDING_MODE))
                        .multiply(ONE_HUNDRED);
                percentUsed = percentUsed
                        .add(((acctPercent.divide(totalPercentValue, SCALE, BIG_DECIMAL_ROUNDING_MODE)))
                                .multiply(ONE_HUNDRED));
            } else {
                // this account is the last account so we have to makeup whatever is left out of 100
                percentToUse = ONE_HUNDRED.subtract(percentUsed);
            }

            PurApAccountingLine newAccountingLine = accountingLine.createBlankAmountsCopy();
            if (LOG.isDebugEnabled()) {
                LOG.debug(methodName + " pct = " + percentToUse);
            }
            newAccountingLine
                    .setAccountLinePercent(percentToUse.setScale(acctPercent.scale(), BIG_DECIMAL_ROUNDING_MODE));
            if (LOG.isDebugEnabled()) {
                LOG.debug(methodName + " adding " + newAccountingLine.getAccountLinePercent());
            }
            newAccounts.add(newAccountingLine);
            logDisplayOnlyTotal = logDisplayOnlyTotal.add(newAccountingLine.getAccountLinePercent());
            if (LOG.isDebugEnabled()) {
                LOG.debug(methodName + " total = " + logDisplayOnlyTotal);
            }
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return newAccounts;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummary(java.util.List)
     */
    @Override
    public List<SourceAccountingLine> generateSummary(List<PurApItem> items) {
        String methodName = "generateSummary()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE,
                ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    @Override
    public List<SourceAccountingLine> generateSummaryTaxableAccounts(List<PurApItem> items) {
        String methodName = "generateSummary()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE,
                ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, true);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryAccounts(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
     */
    @Override
    public List<SummaryAccount> generateSummaryAccounts(PurchasingAccountsPayableDocument document) {
        // always update the amounts first
        updateAccountAmounts(document);
        return generateSummaryAccounts(document.getItems(), ZERO_TOTALS_RETURNED_VALUE, USE_TAX_INCLUDED);
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryAccountsWithNoZeroTotals(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
     */
    @Override
    public List<SummaryAccount> generateSummaryAccountsWithNoZeroTotals(
            PurchasingAccountsPayableDocument document) {
        // always update the amounts first
        updateAccountAmounts(document);
        return generateSummaryAccounts(document.getItems(), ZERO_TOTALS_NOT_RETURNED_VALUE, USE_TAX_INCLUDED);
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryAccountsWithNoZeroTotals(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
     */
    @Override
    public List<SummaryAccount> generateSummaryAccountsWithNoZeroTotalsNoUseTax(
            PurchasingAccountsPayableDocument document) {
        // always update the amounts first
        updateAccountAmounts(document);
        return generateSummaryAccounts(document.getItems(), ZERO_TOTALS_NOT_RETURNED_VALUE, USE_TAX_EXCLUDED);
    }

    /**
     * This creates summary accounts based on a list of items.
     *
     * @param items a list of PurAp Items.
     * @return a list of summary accounts.
     */
    protected List<SummaryAccount> generateSummaryAccounts(List<PurApItem> items, Boolean useZeroTotals,
            Boolean useTaxIncluded) {
        String methodName = "generateSummaryAccounts()";
        List<SummaryAccount> returnList = new ArrayList<SummaryAccount>();
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }

        List<SourceAccountingLine> sourceLines = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE,
                useZeroTotals, ALTERNATE_AMOUNT_NOT_USED, useTaxIncluded, false);
        for (SourceAccountingLine sourceAccountingLine : sourceLines) {
            SummaryAccount summaryAccount = new SummaryAccount();
            summaryAccount.setAccount((SourceAccountingLine) ObjectUtils.deepCopy(sourceAccountingLine));
            for (PurApItem item : items) {
                List<PurApAccountingLine> itemAccounts = item.getSourceAccountingLines();
                for (PurApAccountingLine purApAccountingLine : itemAccounts) {
                    if (purApAccountingLine.accountStringsAreEqual(summaryAccount.getAccount())) {
                        PurApSummaryItem summaryItem = item.getSummaryItem();
                        // If the summaryItem is null, it means the item is not eligible to
                        // be displayed in the Account Summary tab. If it's not null then
                        // we'll set the estimatedEncumberanceAmount and add the item to the
                        // summaryAccount list to be displayed in the Account Summary tab.
                        KualiDecimal amt = KualiDecimal.ZERO;
                        if (ObjectUtils.isNotNull(purApAccountingLine.getAmount())) {
                            amt = purApAccountingLine.getAmount();
                        }
                        if (summaryItem != null) {
                            if (item instanceof OleInvoiceItem) {
                                OleInvoiceItem oleInvoiceItem = (OleInvoiceItem) item;
                                if (!((OleInvoiceItem) item).isDebitItem()) {
                                    summaryItem.setEstimatedEncumberanceAmount(amt.negated());
                                } else {
                                    summaryItem.setEstimatedEncumberanceAmount(amt);
                                }
                            } else {
                                summaryItem.setEstimatedEncumberanceAmount(amt);
                            }
                            summaryAccount.getItems().add(summaryItem);
                            break;
                        }
                    }

                }
            }
            returnList.add(summaryAccount);
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryWithNoZeroTotals(java.util.List)
     */
    @Override
    public List<SourceAccountingLine> generateSummaryWithNoZeroTotals(List<PurApItem> items) {
        String methodName = "generateSummaryWithNoZeroTotals()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE,
                ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED, false);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    /**
     * calls generateSummary with no use tax included
     */
    @Override
    public List<SourceAccountingLine> generateSummaryWithNoZeroTotalsNoUseTax(List<PurApItem> items) {
        String methodName = "generateSummaryWithNoZeroTotalsNoUseTax()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE,
                ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_EXCLUDED, false);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }

        return returnList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryWithNoZeroTotalsUsingAlternateAmount(java.util.List)
     */
    @Override
    public List<SourceAccountingLine> generateSummaryWithNoZeroTotalsUsingAlternateAmount(List<PurApItem> items) {
        String methodName = "generateSummaryWithNoZeroTotals()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, null, ITEM_TYPES_EXCLUDED_VALUE,
                ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_USED, USE_TAX_INCLUDED, false);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryExcludeItemTypes(java.util.List, java.util.Set)
     */
    @Override
    public List<SourceAccountingLine> generateSummaryExcludeItemTypes(List<PurApItem> items,
            Set excludedItemTypeCodes) {
        String methodName = "generateSummaryExcludeItemTypes()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, excludedItemTypeCodes,
                ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED,
                false);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryIncludeItemTypesAndNoZeroTotals(java.util.List,
     *      java.util.Set)
     */
    @Override
    public List<SourceAccountingLine> generateSummaryIncludeItemTypesAndNoZeroTotals(List<PurApItem> items,
            Set includedItemTypeCodes) {
        String methodName = "generateSummaryExcludeItemTypesAndNoZeroTotals()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, includedItemTypeCodes,
                ITEM_TYPES_INCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED,
                USE_TAX_INCLUDED, false);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryIncludeItemTypes(java.util.List, java.util.Set)
     */
    @Override
    public List<SourceAccountingLine> generateSummaryIncludeItemTypes(List<PurApItem> items,
            Set includedItemTypeCodes) {
        String methodName = "generateSummaryIncludeItemTypes()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, includedItemTypeCodes,
                ITEM_TYPES_INCLUDED_VALUE, ZERO_TOTALS_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED, USE_TAX_INCLUDED,
                false);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateSummaryExcludeItemTypesAndNoZeroTotals(java.util.List,
     *      java.util.Set)
     */
    @Override
    public List<SourceAccountingLine> generateSummaryExcludeItemTypesAndNoZeroTotals(List<PurApItem> items,
            Set excludedItemTypeCodes) {
        String methodName = "generateSummaryIncludeItemTypesAndNoZeroTotals()";
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " started");
        }
        List<SourceAccountingLine> returnList = generateAccountSummary(items, excludedItemTypeCodes,
                ITEM_TYPES_EXCLUDED_VALUE, ZERO_TOTALS_NOT_RETURNED_VALUE, ALTERNATE_AMOUNT_NOT_USED,
                USE_TAX_INCLUDED, false);
        if (LOG.isDebugEnabled()) {
            LOG.debug(methodName + " ended");
        }
        return returnList;
    }

    /**
     * Generates an account summary, that is it creates a list of source accounts by rounding up the purap accounts off of the purap
     * items.
     *
     * @param items                    the items to determ
     * @param itemTypeCodes            the item types to determine whether to look at an item in combination with itemTypeCodesAreIncluded
     * @param itemTypeCodesAreIncluded value to tell whether the itemTypeCodes parameter lists inclusion or exclusion variables
     * @param useZeroTotals            whether to include items with a zero dollar total
     * @param useAlternateAmount       an alternate amount used in certain cases for GL entry
     * @return a list of source accounts
     */
    protected List<SourceAccountingLine> generateAccountSummary(List<PurApItem> items, Set<String> itemTypeCodes,
            Boolean itemTypeCodesAreIncluded, Boolean useZeroTotals, Boolean useAlternateAmount,
            Boolean useTaxIncluded, Boolean taxableOnly) {
        List<PurApItem> itemsToProcess = getProcessablePurapItems(items, itemTypeCodes, itemTypeCodesAreIncluded,
                useZeroTotals);
        Map<PurApAccountingLine, KualiDecimal> accountMap = new HashMap<PurApAccountingLine, KualiDecimal>();

        for (PurApItem currentItem : itemsToProcess) {
            if (PurApItemUtils.checkItemActive(currentItem)) {
                List<PurApAccountingLine> sourceAccountingLines = currentItem.getSourceAccountingLines();

                // skip if item is not taxable and taxable only flag has been set
                if (taxableOnly) {
                    PurchasingAccountsPayableDocument document = currentItem.getPurapDocument();
                    if (!purapService.isTaxableForSummary(document.isUseTaxIndicator(),
                            purapService.getDeliveryState(document), currentItem)) {
                        continue;
                    }
                }

                if (!useTaxIncluded) {
                    // if no use tax set the source accounting lines to a clone so we can update
                    // them to be based on the non tax amount
                    if (currentItem instanceof OleInvoiceItem) {
                        OleInvoiceItem invoiceItem = (OleInvoiceItem) currentItem;
                        if (invoiceItem.getItemType().isQuantityBasedGeneralLedgerIndicator()
                                && invoiceItem.getRelatedViews() != null) {
                            // invoiceItem.setRelatedViews(null);
                            PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(invoiceItem);
                            sourceAccountingLines = cloneItem.getSourceAccountingLines();
                            updateAccountAmountsWithTotal(sourceAccountingLines, invoiceItem.getTotalRemitAmount());
                        } else {
                            PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(currentItem);
                            sourceAccountingLines = cloneItem.getSourceAccountingLines();
                            updateAccountAmountsWithTotal(sourceAccountingLines, currentItem.getTotalRemitAmount());
                        }
                    } else {
                        PurApItem cloneItem = (PurApItem) ObjectUtils.deepCopy(currentItem);
                        sourceAccountingLines = cloneItem.getSourceAccountingLines();
                        updateAccountAmountsWithTotal(sourceAccountingLines, currentItem.getTotalRemitAmount());
                    }
                }

                for (PurApAccountingLine account : sourceAccountingLines) {

                    // skip account if not taxable and taxable only flag is set
                    if (taxableOnly) {
                        PurchasingAccountsPayableDocument document = currentItem.getPurapDocument();
                        // check if account is not taxable, if not skip this account
                        if (!purapService.isAccountingLineTaxable(account,
                                purapService.isDeliveryStateTaxable(purapService.getDeliveryState(document)))) {
                            continue;
                        }
                    }

                    KualiDecimal total = KualiDecimal.ZERO;
                    if (account instanceof InvoiceAccount) {
                        if (((InvoiceAccount) account).getInvoiceItem() != null) {
                            if (((OleInvoiceItem) ((InvoiceAccount) account).getInvoiceItem()).isDebitItem()) {
                                if (account.getOrganizationReferenceId() != null
                                        && account.getOrganizationReferenceId().isEmpty()) {
                                    account.setOrganizationReferenceId(null);
                                }
                                if (accountMap.containsKey(account)) {
                                    total = accountMap.get(account);
                                }

                                if (useAlternateAmount) {
                                    total = total.add(account.getAlternateAmountForGLEntryCreation());
                                } else {
                                    if (ObjectUtils.isNotNull(account.getAmount())) {
                                        total = total.add(account.getAmount());
                                    }
                                }
                            } else {
                                if (accountMap.containsKey(account)) {
                                    total = accountMap.get(account);
                                }

                                if (useAlternateAmount) {
                                    total = total.subtract(account.getAlternateAmountForGLEntryCreation());
                                } else {
                                    if (ObjectUtils.isNotNull(account.getAmount())) {
                                        total = total.subtract(account.getAmount());
                                    }
                                }
                            }
                        }

                    } else {
                        // getting the total to set on the account
                        if (accountMap.containsKey(account)) {
                            total = accountMap.get(account);
                        }

                        if (useAlternateAmount) {
                            total = total.add(account.getAlternateAmountForGLEntryCreation());
                        } else {
                            if (ObjectUtils.isNotNull(account.getAmount())) {
                                total = total.add(account.getAmount());
                            }
                        }
                    }
                    accountMap.put(account, total);
                }
            }
        }

        // convert list of PurApAccountingLine objects to SourceAccountingLineObjects
        Iterator<PurApAccountingLine> iterator = accountMap.keySet().iterator();
        List<SourceAccountingLine> sourceAccounts = new ArrayList<SourceAccountingLine>();
        for (Iterator<PurApAccountingLine> iter = iterator; iter.hasNext();) {
            PurApAccountingLine accountToConvert = iter.next();
            if (accountToConvert.isEmpty()) {
                String errorMessage = "Found an 'empty' account in summary generation "
                        + accountToConvert.toString();
                LOG.error("generateAccountSummary() " + errorMessage);
                throw new RuntimeException(errorMessage);
            }
            KualiDecimal sourceLineTotal = accountMap.get(accountToConvert);
            SourceAccountingLine sourceLine = accountToConvert.generateSourceAccountingLine();
            sourceLine.setAmount(sourceLineTotal);
            sourceAccounts.add(sourceLine);
        }

        // sort the sourceAccounts list first by account number, then by object code, ignoring chart code
        Collections.sort(sourceAccounts, new Comparator<SourceAccountingLine>() {
            @Override
            public int compare(SourceAccountingLine sal1, SourceAccountingLine sal2) {
                int compare = 0;
                if (sal1 != null && sal2 != null) {
                    if (sal1.getAccountNumber() != null && sal2.getAccountNumber() != null) {
                        compare = sal1.getAccountNumber().compareTo(sal2.getAccountNumber());
                        if (compare == 0) {
                            if (sal1.getFinancialObjectCode() != null && sal2.getFinancialObjectCode() != null) {
                                compare = sal1.getFinancialObjectCode().compareTo(sal2.getFinancialObjectCode());
                            }
                        }
                    }
                }
                return compare;
            }
        });

        return sourceAccounts;
    }

    /**
     * This method takes a list of {@link PurchasingApItem} objects and parses through them to see if each one should be processed
     * according the the other variables passed in.<br>
     * <br>
     * Example 1:<br>
     * items = "ITEM", "SITM", "FRHT", "SPHD"<br>
     * itemTypeCodes = "FRHT"<br>
     * itemTypeCodesAreIncluded = ITEM_TYPES_EXCLUDED_VALUE<br>
     * return items "ITEM", "SITM", "FRHT", "SPHD"<br>
     * <br>
     * <br>
     * Example 2:<br>
     * items = "ITEM", "SITM", "FRHT", "SPHD"<br>
     * itemTypeCodes = "ITEM","FRHT"<br>
     * itemTypeCodesAreIncluded = ITEM_TYPES_INCLUDED_VALUE<br>
     * return items "ITEM", "FRHT"<br>
     *
     * @param items                    - list of {@link PurchasingApItem} objects that need to be parsed
     * @param itemTypeCodes            - list of {@link org.kuali.ole.module.purap.businessobject.ItemType} codes used in conjunction with
     *                                 itemTypeCodesAreIncluded parameter
     * @param itemTypeCodesAreIncluded - value to tell whether the itemTypeCodes parameter lists inclusion or exclusion variables
     *                                 (see {@link #ITEM_TYPES_INCLUDED_VALUE})
     * @param useZeroTotals            - value to tell whether to include zero dollar items (see {@link #ZERO_TOTALS_RETURNED_VALUE})
     * @return a list of {@link PurchasingApItem} objects that should be used for processing by calling method
     */
    protected List<PurApItem> getProcessablePurapItems(List<PurApItem> items, Set itemTypeCodes,
            Boolean itemTypeCodesAreIncluded, Boolean useZeroTotals) {
        String methodName = "getProcessablePurapItems()";
        List<PurApItem> newItemList = new ArrayList<PurApItem>();
        // error out if we have an invalid 'itemTypeCodesAreIncluded' value
        if ((!(ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded)))
                && (!(ITEM_TYPES_EXCLUDED_VALUE.equals(itemTypeCodesAreIncluded)))) {
            throwRuntimeException(methodName,
                    "Invalid parameter found while trying to find processable items for dealing with purchasing/accounts payable accounts");
        }
        for (PurApItem currentItem : items) {
            if ((itemTypeCodes != null) && (!(itemTypeCodes.isEmpty()))) {
                // we have at least one entry in our item type code list
                boolean foundMatchInList = false;
                // check to see if this item type code is in the list
                for (Iterator iterator = itemTypeCodes.iterator(); iterator.hasNext();) {
                    String itemTypeCode = (String) iterator.next();
                    // include this item if it's in the included list
                    if (itemTypeCode.equals(currentItem.getItemType().getItemTypeCode())) {
                        foundMatchInList = true;
                        break;
                    }
                }
                // check to see if item type code was found and if the list is describing included or excluded item types
                if ((foundMatchInList) && (ITEM_TYPES_EXCLUDED_VALUE.equals(itemTypeCodesAreIncluded))) {
                    // this item type code is in the list
                    // this item type code is excluded so we skip it
                    continue; // skips current item
                } else if ((!foundMatchInList) && (ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded))) {
                    // this item type code is not in the list
                    // this item type code is not included so we skip it
                    continue; // skips current item
                }
            } else {
                // the item type code list is empty
                if (ITEM_TYPES_INCLUDED_VALUE.equals(itemTypeCodesAreIncluded)) {
                    // the item type code list is empty and the list is supposed to contain the item types to include
                    throwRuntimeException(methodName,
                            "Invalid parameter and list of items found while trying to find processable items for dealing with purchasing/accounts payable accounts");
                }
            }
            if ((ZERO_TOTALS_NOT_RETURNED_VALUE.equals(useZeroTotals))
                    && (ObjectUtils.isNull(currentItem.getExtendedPrice())
                            || ((KualiDecimal.ZERO.compareTo(currentItem.getExtendedPrice())) == 0))) {
                // if we don't return zero dollar items then skip this one
                continue;
            }
            newItemList.add(currentItem);
        }
        return newItemList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#updateAccountAmounts(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
     */
    @Override
    public void updateAccountAmounts(PurchasingAccountsPayableDocument document) {

        PurchasingAccountsPayableDocumentBase purApDocument = (PurchasingAccountsPayableDocumentBase) document;
        String accountDistributionMethod = purApDocument.getAccountDistributionMethod();

        KualiRuleService kualiRuleService = SpringContext.getBean(KualiRuleService.class);
        // olePaymentRequestDocument = (OlePaymentRequestDocument)document;
        // the percent at fiscal approve
        // don't update if past the AP review level
        if ((document instanceof PaymentRequestDocument) && purapService.isFullDocumentEntryCompleted(document)) {
            // update the percent but don't update the amounts if preq and past full entry
            convertMoneyToPercent((PaymentRequestDocument) document);
            return;
        }
        if ((document instanceof OleInvoiceDocument) && purapService.isFullDocumentEntryCompleted(document)) {
            // update the percent but don't update the amounts if preq and past full entry
            convertMoneyToPercent((OleInvoiceDocument) document);
            return;
        }
        document.fixItemReferences();

        // OLE-3405 : disabling KFS Proportional/sequential distribution methods
        //if distribution method is sequential and document is PREQ or VCM then...
        //          if (((document instanceof PaymentRequestDocument)
        //                  || (document instanceof VendorCreditMemoDocument))
        //                  && PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) {
        //              if (document instanceof VendorCreditMemoDocument) {
        //                  VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) document;
        //                  cmDocument.updateExtendedPriceOnItems();
        //              }
        //
        //              // update the accounts amounts for PREQ and distribution method = sequential
        //              for (PurApItem item : document.getItems()) {
        //                  updatePreqItemAccountAmounts(item);
        //              }
        //
        //              return;
        //          }

        // OLE-3405 : disabling KFS Proportional/sequential distribution methods
        //if distribution method is proportional and document is PREQ or VCM then...
        //          if (((document instanceof PaymentRequestDocument) || (document instanceof VendorCreditMemoDocument)) && PurapConstants.AccountDistributionMethodCodes.PROPORTIONAL_CODE.equalsIgnoreCase(accountDistributionMethod)) {
        //              // update the accounts amounts for PREQ and distribution method = sequential
        //              if (document instanceof VendorCreditMemoDocument) {
        //                  VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) document;
        //                  cmDocument.updateExtendedPriceOnItems();
        //              }
        for (PurApItem purApItem : document.getItems()) {
            for (PurApAccountingLine oldSourceAccountingLine : purApItem.getSourceAccountingLines()) {
                if (oldSourceAccountingLine instanceof OleRequisitionAccount) {
                    ((OleRequisitionAccount) oldSourceAccountingLine)
                            .setExistingAmount(oldSourceAccountingLine.getAmount());
                } else if (oldSourceAccountingLine instanceof OlePurchaseOrderAccount) {
                    ((OlePurchaseOrderAccount) oldSourceAccountingLine)
                            .setExistingAmount(oldSourceAccountingLine.getAmount());
                } else if (oldSourceAccountingLine instanceof PaymentRequestAccount) {
                    ((PaymentRequestAccount) oldSourceAccountingLine)
                            .setExistingAmount(oldSourceAccountingLine.getAmount());
                } else if (oldSourceAccountingLine instanceof InvoiceAccount) {
                    ((InvoiceAccount) oldSourceAccountingLine)
                            .setExistingAmount(oldSourceAccountingLine.getAmount());
                }
            }
        }
        for (PurApItem item : document.getItems()) {
            // OLE-3405 : disabling KFS Proportional/sequential distribution methods
            //                  boolean rulePassed = true;
            //                  // check any business rules
            //                  rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item));
            //
            //                  if (rulePassed) {
            //                      updatePreqProportionalItemAccountAmounts(item);
            updateItemAccountAmounts(item);
            if (item instanceof OlePaymentRequestItem) {
                updateItemAccountAmountsForAdditionalCharge(item, (OlePaymentRequestDocument) document);
            } else if (item instanceof OleCreditMemoItem) {
                updateItemAccountAmountsForAdditionalCharge(item, (OleVendorCreditMemoDocument) document);
            } else if (item instanceof OleInvoiceItem) {
                updateItemAccountAmountsForAdditionalCharge(item, (OleInvoiceDocument) document);
            } else {
                updateItemAccountAmounts(item);
                //                          updatePreqProportionalItemAccountAmounts(item);
            }
            //                  }
        }

        // OLE-3405 : disabling KFS Proportional/sequential distribution methods
        //              return;
        //          }

        // OLE-3405 : disabling KFS Proportional/sequential distribution methods
        //No recalculate if the account distribution method code is equal to "S" sequential ON REQ or POs..
        //          if (PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) {
        //              for (PurApItem item : document.getItems()) {
        //                  boolean rulePassed = true;
        //                  // check any business rules
        //                  rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item));
        //
        //                  return;
        //              }
        //          }
        //
        //          //do recalculate only if the account distribution method code is not equal to "S" sequential method.
        //          if (!PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) {
        //              for (PurApItem item : document.getItems()) {
        //                  boolean rulePassed = true;
        //                  // check any business rules
        //                  rulePassed &= kualiRuleService.applyRules(new PurchasingAccountsPayableItemPreCalculateEvent(document, item));
        //
        //                  if (rulePassed) {
        //              updateItemAccountAmounts(item);
        //          }
        //      }
        //          }
    }

    public void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OlePaymentRequestDocument document) {
        List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
        OlePaymentRequestItem olePaymentRequestItem = (OlePaymentRequestItem) item;
        KualiDecimal prorateSurcharge = new KualiDecimal();
        KualiDecimal qty = KualiDecimal.ZERO;
        if (olePaymentRequestItem.getItemSurcharge() == null) {
            prorateSurcharge = KualiDecimal.ZERO;
        } else {
            qty = olePaymentRequestItem.getItemQuantity();
            prorateSurcharge = new KualiDecimal(olePaymentRequestItem.getItemSurcharge());
        }
        KualiDecimal totalAmount = item.getTotalAmount();
        totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty));
        updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount);
    }

    public void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OleInvoiceDocument document) {
        List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
        OleInvoiceItem oleInvoiceItem = (OleInvoiceItem) item;
        KualiDecimal prorateSurcharge = new KualiDecimal();
        KualiDecimal qty = KualiDecimal.ZERO;
        if (oleInvoiceItem.getItemSurcharge() == null) {
            prorateSurcharge = KualiDecimal.ZERO;
        } else {
            qty = oleInvoiceItem.getItemQuantity();
            prorateSurcharge = new KualiDecimal(oleInvoiceItem.getItemSurcharge());
        }
        KualiDecimal totalAmount = item.getTotalAmount();
        totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty));
        updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount);
    }

    private void updateItemAccountAmountsForAdditionalCharge(PurApItem item, OleVendorCreditMemoDocument document) {

        List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
        OleCreditMemoItem oleCreditMemoItem = (OleCreditMemoItem) item;
        KualiDecimal prorateSurcharge = new KualiDecimal();
        KualiDecimal qty = KualiDecimal.ZERO;
        if (oleCreditMemoItem.getItemSurcharge() == null) {
            prorateSurcharge = KualiDecimal.ZERO;
        } else {
            qty = oleCreditMemoItem.getItemQuantity();
            prorateSurcharge = new KualiDecimal(oleCreditMemoItem.getItemSurcharge());
        }
        KualiDecimal totalAmount = item.getTotalAmount();
        totalAmount = totalAmount.subtract(prorateSurcharge.multiply(qty));
        updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount);

    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#updateItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem)
     */
    @Override
    public void updateItemAccountAmounts(PurApItem item) {
        List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
        KualiDecimal totalAmount = item.getTotalAmount();
        if (item.getItemType().isAdditionalChargeIndicator()) {
            updateAccountAmountsWithTotalForAdditionalCharge(sourceAccountingLines, totalAmount);
        } else {
            updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount);
        }
    }

    /*public void updateItemAccountAmountsForAdditionalCharge(PurApItem item) {
    List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
    KualiDecimal totalAmount = item.getTotalAmount();
        updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount);
    }*/

    /**
     * calculates values for a list of accounting lines based on an amount
     *
     * @param sourceAccountingLines
     * @param totalAmount
     */

    public <T extends PurApAccountingLine> void updateAccountAmountsWithTotalForAdditionalCharge(
            List<T> sourceAccountingLines, KualiDecimal totalAmount) {
        if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) {

            KualiDecimal accountTotal = KualiDecimal.ZERO;
            T lastAccount = null;

            for (T account : sourceAccountingLines) {
                if (ObjectUtils.isNotNull(account.getAccountLinePercent())) {
                    BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString())
                            .divide(new BigDecimal(100));
                    account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString()))
                            .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)));
                } else {
                    account.setAmount(KualiDecimal.ZERO);
                }
                accountTotal = accountTotal.add(account.getAmount());
                lastAccount = account;
            }

            // put excess on last account
            if (lastAccount != null) {
                KualiDecimal difference = totalAmount.subtract(accountTotal);
                lastAccount.setAmount(lastAccount.getAmount().add(difference));
            }
        } else {
            // zero out if extended price is zero
            for (T account : sourceAccountingLines) {
                account.setAmount(KualiDecimal.ZERO);
            }
        }
    }

    public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal2(List<T> sourceAccountingLines,
            KualiDecimal totalAmount) {
        if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) {

            KualiDecimal accountTotal = KualiDecimal.ZERO;
            T lastAccount = null;

            for (T account : sourceAccountingLines) {
                if (ObjectUtils.isNotNull(account.getAccountLinePercent())) {
                    //    OlePaymentRequestItem item = new OlePaymentRequestItem();
                    BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString())
                            .divide(new BigDecimal(100));
                    account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString()))
                            .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)));
                } else {
                    account.setAmount(KualiDecimal.ZERO);
                }
                accountTotal = accountTotal.add(account.getAmount());
                lastAccount = account;
            }

            // put excess on last account
            if (lastAccount != null) {
                KualiDecimal difference = totalAmount.subtract(accountTotal);
                lastAccount.setAmount(lastAccount.getAmount().add(difference));
            }
        } else {
            // zero out if extended price is zero
            for (T account : sourceAccountingLines) {
                account.setAmount(KualiDecimal.ZERO);
            }
        }
    }

    /**
     * calculates values for a list of accounting lines based on an amount
     *
     * @param sourceAccountingLines
     * @param totalAmount
     */
    @Override
    public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal(List<T> sourceAccountingLines,
            KualiDecimal totalAmount) {
        updateAccountAmountsWithTotal(sourceAccountingLines, totalAmount, new KualiDecimal(0));
    }

    /**
     * calculates values for a list of accounting lines based on an amount taking discount into account
     *
     * @param sourceAccountingLines
     * @param totalAmount
     * @param discountAmount
     */
    @Override
    public <T extends PurApAccountingLine> void updateAccountAmountsWithTotal(List<T> sourceAccountingLines,
            KualiDecimal totalAmount, KualiDecimal discountAmount) {

        // if we have a discount, then we need to base the amounts on the discount, but the percent on the total
        boolean noDiscount = true;
        if ((discountAmount != null) && KualiDecimal.ZERO.compareTo(discountAmount) != 0) {
            noDiscount = false;
        }

        if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) {

            KualiDecimal accountTotal = KualiDecimal.ZERO;
            BigDecimal accountTotalPercent = BigDecimal.ZERO;
            T lastAccount = null;

            for (T account : sourceAccountingLines) {
                if (ObjectUtils.isNotNull(account.getAccountLinePercent())
                        || ObjectUtils.isNotNull(account.getAmount())) {
                    if (ObjectUtils.isNotNull(account.getAmount())
                            && account.getAmount().isGreaterThan(KualiDecimal.ZERO)) {
                        KualiDecimal amt = account.getAmount();
                        KualiDecimal calculatedPercent = new KualiDecimal(
                                amt.multiply(new KualiDecimal(100)).divide(totalAmount).toString());
                        account.setAccountLinePercent(
                                calculatedPercent.bigDecimalValue().setScale(BIG_DECIMAL_SCALE));
                    }

                    if (ObjectUtils.isNotNull(account.getAccountLinePercent())) {
                        BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString())
                                .divide(new BigDecimal(100));
                        if (noDiscount) {
                            if (ObjectUtils.isNull(account.getAmount()) || account.getAmount().isZero()) {
                                account.setAmount(
                                        new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString()))
                                                .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)));
                            }
                        } else {
                            account.setAmount(
                                    new KualiDecimal(pct.multiply(new BigDecimal(discountAmount.toString()))
                                            .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)));
                        }
                    }
                }

                if (ObjectUtils.isNotNull(account.getAmount())) {
                    accountTotal = accountTotal.add(account.getAmount());
                }

                if (ObjectUtils.isNotNull(account.getAccountLinePercent())) {
                    accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent());
                }

                lastAccount = account;
            }

            // put excess on last account
            if (lastAccount != null) {
                KualiDecimal difference = new KualiDecimal(0);
                if (noDiscount) {
                    difference = totalAmount.subtract(accountTotal);
                } else {
                    difference = discountAmount.subtract(accountTotal);
                }
                if (ObjectUtils.isNotNull(lastAccount.getAmount())) {
                    if ((difference.abs()).isLessEqual(new KualiDecimal(1).multiply(
                            new KualiDecimal(sourceAccountingLines.size()).divide(new KualiDecimal(2))))) {
                        lastAccount.setAmount(lastAccount.getAmount().add(difference));
                    } else {
                        lastAccount.setAmount(lastAccount.getAmount());
                    }
                }
                BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent)
                        .setScale(BIG_DECIMAL_SCALE, BigDecimal.ROUND_CEILING);
                if (ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) {

                    KualiDecimal differencePercent = (((new KualiDecimal(accountTotalPercent))
                            .subtract(new KualiDecimal(100))).abs());
                    if ((differencePercent.abs()).isLessEqual(new KualiDecimal(1).multiply(
                            (new KualiDecimal(sourceAccountingLines.size()).divide(new KualiDecimal(2)))))) {
                        lastAccount
                                .setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference));
                    } else {
                        lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent());
                    }
                }
            }
        } else {
            // zero out if extended price is zero
            for (T account : sourceAccountingLines) {
                if (ObjectUtils.isNotNull(account.getAmount())) {
                    account.setAmount(KualiDecimal.ZERO);
                }
            }
        }
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#updatePreqProportionalItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem)
     */
    @Override
    public void updatePreqProportionalItemAccountAmounts(PurApItem item) {
        List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
        KualiDecimal totalAmount = item.getTotalAmount();

        updatePreqProporationalAccountAmountsWithTotal(sourceAccountingLines, totalAmount);
    }

    /**
     * calculates values for a list of accounting lines based on an amount for proportional method
     *
     * @param sourceAccountingLines
     * @param totalAmount
     */
    @Override
    public <T extends PurApAccountingLine> void updatePreqProporationalAccountAmountsWithTotal(
            List<T> sourceAccountingLines, KualiDecimal totalAmount) {
        if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) {
            KualiDecimal accountTotal = KualiDecimal.ZERO;
            BigDecimal accountTotalPercent = BigDecimal.ZERO;
            T lastAccount = null;

            for (T account : sourceAccountingLines) {
                if (ObjectUtils.isNotNull(account.getAccountLinePercent())
                        || ObjectUtils.isNotNull(account.getAmount())) {
                    if (ObjectUtils.isNotNull(account.getAccountLinePercent())) {
                        BigDecimal pct = new BigDecimal(account.getAccountLinePercent().toString())
                                .divide(new BigDecimal(100));
                        account.setAmount(new KualiDecimal(pct.multiply(new BigDecimal(totalAmount.toString()))
                                .setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)));
                    }
                }

                if (ObjectUtils.isNotNull(account.getAmount())) {
                    accountTotal = accountTotal.add(account.getAmount());
                }
                if (ObjectUtils.isNotNull(account.getAccountLinePercent())) {
                    accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent());
                }

                lastAccount = account;
            }

            // put excess on last account
            if (lastAccount != null) {
                KualiDecimal difference = totalAmount.subtract(accountTotal);
                if (ObjectUtils.isNotNull(lastAccount.getAmount())) {
                    lastAccount.setAmount(lastAccount.getAmount().add(difference));
                }

                BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent)
                        .setScale(BIG_DECIMAL_SCALE, BigDecimal.ROUND_CEILING);
                if (ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) {
                    lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference));
                }
            }
        }
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#updatePreqItemAccountAmounts(org.kuali.ole.module.purap.businessobject.PurApItem)
     */
    @Override
    public void updatePreqItemAccountAmounts(PurApItem item) {
        List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
        KualiDecimal totalAmount = item.getTotalAmount();

        updatePreqAccountAmountsWithTotal(sourceAccountingLines, totalAmount);
    }

    /**
     * calculates values for a list of accounting lines based on an amount.  Preq item's extended
     * cost is distributed to the accounting lines.
     *
     * @param sourceAccountingLines
     * @param totalAmount
     */
    @Override
    public <T extends PurApAccountingLine> void updatePreqAccountAmountsWithTotal(List<T> sourceAccountingLines,
            KualiDecimal totalAmount) {
        if ((totalAmount != null) && KualiDecimal.ZERO.compareTo(totalAmount) != 0) {
            KualiDecimal accountTotal = KualiDecimal.ZERO;
            BigDecimal accountTotalPercent = BigDecimal.ZERO;
            T lastAccount = null;

            for (T account : sourceAccountingLines) {
                //look at lines where amount is non-zero..
                if (account.getAmount().isGreaterThan(KualiDecimal.ZERO)) {
                    if (totalAmount.isZero()) {
                        account.setAmount(KualiDecimal.ZERO);
                    } else {
                        if (account.getAmount().isGreaterThan(totalAmount)) {
                            account.setAmount(totalAmount);
                        }
                    }
                }

                totalAmount = totalAmount.subtract(account.getAmount());
            }

            if (totalAmount.isGreaterThan(KualiDecimal.ZERO)) {
                for (T account : sourceAccountingLines) {
                    if (account.getAmount().isZero()
                            || account.getAccountLinePercent().compareTo(BigDecimal.ZERO) == 1) {
                        KualiDecimal priorAmount = account.getAmount();
                        account.setAmount(account.getAmount().add(new KualiDecimal(account.getAccountLinePercent())
                                .multiply(totalAmount).divide(new KualiDecimal(100))));
                        accountTotal = accountTotal.add(account.getAmount().subtract(priorAmount));
                        lastAccount = account;
                    }
                }
            }

            accountTotal = totalAmount.subtract(accountTotal);

            if (accountTotal.isGreaterThan(KualiDecimal.ZERO) && ObjectUtils.isNotNull(lastAccount)) {
                //add the difference to the last overage account....
                lastAccount.setAmount(lastAccount.getAmount().add(accountTotal));
            }
        }
    }

    public List<PurApAccountingLine> generatePercentSummary(PurchasingAccountsPayableDocument purapDoc) {
        List<PurApAccountingLine> accounts = new ArrayList<PurApAccountingLine>();
        for (PurApItem currentItem : purapDoc.getItems()) {
            if (PurApItemUtils.checkItemActive(currentItem)) {
                for (PurApAccountingLine account : currentItem.getSourceAccountingLines()) {
                    boolean thisAccountAlreadyInSet = false;
                    for (Object element : accounts) {
                        PurApAccountingLine alreadyAddedAccount = (PurApAccountingLine) element;

                        if (alreadyAddedAccount.accountStringsAreEqual(account)) {
                            BigDecimal alreadyAddedAccountLinePercent = BigDecimal.ZERO;
                            if (ObjectUtils.isNotNull(alreadyAddedAccount.getAccountLinePercent())) {
                                alreadyAddedAccountLinePercent = alreadyAddedAccount.getAccountLinePercent();
                            }
                            BigDecimal accountLinePercent = BigDecimal.ZERO;
                            if (ObjectUtils.isNotNull(account.getAccountLinePercent())) {
                                accountLinePercent = account.getAccountLinePercent();
                            }

                            alreadyAddedAccount
                                    .setAccountLinePercent(alreadyAddedAccountLinePercent.add(accountLinePercent));

                            thisAccountAlreadyInSet = true;
                            break;
                        }
                    }
                    if (!thisAccountAlreadyInSet) {
                        PurApAccountingLine accountToAdd = (PurApAccountingLine) ObjectUtils.deepCopy(account);
                        accounts.add(accountToAdd);
                    }
                }
            }
        }
        return accounts;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#convertMoneyToPercent(org.kuali.ole.module.purap.document.PaymentRequestDocument)
     */
    @Override
    public void convertMoneyToPercent(PaymentRequestDocument pr) {
        LOG.debug("convertMoneyToPercent() started");

        int itemNbr = 0;

        for (Iterator<PaymentRequestItem> iter = pr.getItems().iterator(); iter.hasNext();) {
            PaymentRequestItem item = iter.next();

            itemNbr++;
            String identifier = item.getItemIdentifierString();

            if (item.getTotalAmount() != null && item.getTotalAmount().isNonZero()) {
                int numOfAccounts = item.getSourceAccountingLines().size();
                BigDecimal percentTotal = BigDecimal.ZERO;
                KualiDecimal accountTotal = KualiDecimal.ZERO;
                int accountIdentifier = 0;

                KualiDecimal addChargeItem = KualiDecimal.ZERO;
                KualiDecimal lineItemPreTaxTotal = KualiDecimal.ZERO;
                /* KualiDecimal prorateSurcharge = KualiDecimal.ZERO;
                 if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice() != null && item.getExtendedPrice().compareTo(KualiDecimal.ZERO) != 0) {
                if (((OlePaymentRequestItem) item).getItemSurcharge() != null) {
                    prorateSurcharge = new KualiDecimal(((OlePaymentRequestItem) item).getItemSurcharge()).multiply(item.getItemQuantity());
                }
                 }*/
                PurApAccountingLine lastAccount = null;
                BigDecimal accountTotalPercent = BigDecimal.ZERO;

                for (PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) {
                    accountIdentifier++;
                    PaymentRequestAccount account = (PaymentRequestAccount) purApAccountingLine;

                    // account.getAmount returns the wrong value for trade in source accounting lines...
                    KualiDecimal accountAmount = KualiDecimal.ZERO;
                    if (ObjectUtils.isNotNull(account.getAmount())) {
                        accountAmount = account.getAmount();
                    }

                    BigDecimal tmpPercent = BigDecimal.ZERO;
                    KualiDecimal extendedPrice = item.getTotalAmount();
                    tmpPercent = accountAmount.bigDecimalValue().divide(extendedPrice.bigDecimalValue(),
                            PurapConstants.CREDITMEMO_PRORATION_SCALE.intValue(), KualiDecimal.ROUND_BEHAVIOR);

                    if (accountIdentifier == numOfAccounts) {
                        // if on last account, calculate the percent by subtracting current percent total from 1
                        tmpPercent = BigDecimal.ONE.subtract(percentTotal);
                    }

                    // test that the above amount is correct, if so just check that the total of all these matches the item total
                    BigDecimal calcAmountBd = tmpPercent.multiply(extendedPrice.bigDecimalValue());
                    calcAmountBd = calcAmountBd.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR);
                    KualiDecimal calcAmount = new KualiDecimal(calcAmountBd);
                    //calcAmount = calcAmount.subtract(prorateSurcharge);
                    if (calcAmount.compareTo(accountAmount) != 0) {
                        // rounding error
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("convertMoneyToPercent() Rounding error on " + account);
                        }
                        String param1 = identifier + "." + accountIdentifier;
                        String param2 = calcAmount.bigDecimalValue().subtract(accountAmount.bigDecimalValue())
                                .toString();
                        GlobalVariables.getMessageMap().putError(item.getItemIdentifierString(),
                                PurapKeyConstants.ERROR_ITEM_ACCOUNTING_ROUNDING, param1, param2);
                        account.setAmount(calcAmount);
                    }

                    // update percent
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("convertMoneyToPercent() updating percent to " + tmpPercent);
                    }
                    account.setAccountLinePercent(tmpPercent.multiply(new BigDecimal(100)));
                    accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent());
                    lastAccount = account;
                    // check total based on adjusted amount
                    accountTotal = accountTotal.add(calcAmount);
                    percentTotal = percentTotal.add(tmpPercent);
                }
                BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent)
                        .setScale(BIG_DECIMAL_SCALE, BigDecimal.ROUND_CEILING);
                if (ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) {
                    KualiDecimal differencePercent = (((new KualiDecimal(accountTotalPercent))
                            .subtract(new KualiDecimal(100))).abs());
                    if ((differencePercent.abs()).isLessEqual(
                            new KualiDecimal(1).multiply((new KualiDecimal(item.getSourceAccountingLines().size())
                                    .divide(new KualiDecimal(2)))))) {
                        lastAccount
                                .setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference));
                    } else {
                        lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent());
                    }
                }
            }
        }
    }

    @Override
    public void convertMoneyToPercent(InvoiceDocument inv) {
        LOG.debug("convertMoneyToPercent() started");

        int itemNbr = 0;

        for (Iterator<InvoiceItem> iter = inv.getItems().iterator(); iter.hasNext();) {
            InvoiceItem item = iter.next();

            itemNbr++;
            String identifier = item.getItemIdentifierString();

            if (item.getTotalAmount() != null && item.getTotalAmount().isNonZero()) {
                int numOfAccounts = item.getSourceAccountingLines().size();
                BigDecimal percentTotal = BigDecimal.ZERO;
                KualiDecimal accountTotal = KualiDecimal.ZERO;
                int accountIdentifier = 0;

                KualiDecimal addChargeItem = KualiDecimal.ZERO;
                KualiDecimal lineItemPreTaxTotal = KualiDecimal.ZERO;
                KualiDecimal prorateSurcharge = KualiDecimal.ZERO;
                if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice() != null
                        && item.getExtendedPrice().compareTo(KualiDecimal.ZERO) != 0) {
                    if (((OleInvoiceItem) item).getItemSurcharge() != null) {
                        prorateSurcharge = new KualiDecimal(((OleInvoiceItem) item).getItemSurcharge())
                                .multiply(item.getItemQuantity());
                    }
                }
                PurApAccountingLine lastAccount = null;
                BigDecimal accountTotalPercent = BigDecimal.ZERO;
                for (PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) {
                    accountIdentifier++;
                    InvoiceAccount account = (InvoiceAccount) purApAccountingLine;

                    // account.getAmount returns the wrong value for trade in source accounting lines...
                    KualiDecimal accountAmount = KualiDecimal.ZERO;
                    if (ObjectUtils.isNotNull(account.getAmount())) {
                        accountAmount = account.getAmount();
                    }

                    BigDecimal tmpPercent = BigDecimal.ZERO;
                    KualiDecimal extendedPrice = item.getTotalAmount();
                    tmpPercent = accountAmount.bigDecimalValue().divide(extendedPrice.bigDecimalValue(),
                            PurapConstants.CREDITMEMO_PRORATION_SCALE.intValue(), KualiDecimal.ROUND_BEHAVIOR);

                    if (accountIdentifier == numOfAccounts) {
                        // if on last account, calculate the percent by subtracting current percent total from 1
                        tmpPercent = BigDecimal.ONE.subtract(percentTotal);
                    }

                    // test that the above amount is correct, if so just check that the total of all these matches the item total
                    BigDecimal calcAmountBd = tmpPercent.multiply(extendedPrice.bigDecimalValue());
                    calcAmountBd = calcAmountBd.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR);
                    KualiDecimal calcAmount = new KualiDecimal(calcAmountBd);
                    calcAmount = calcAmount.subtract(prorateSurcharge);
                    if (calcAmount.compareTo(accountAmount) != 0) {
                        // rounding error
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("convertMoneyToPercent() Rounding error on " + account);
                        }
                        String param1 = identifier + "." + accountIdentifier;
                        String param2 = calcAmount.bigDecimalValue().subtract(accountAmount.bigDecimalValue())
                                .toString();
                        GlobalVariables.getMessageMap().putError(item.getItemIdentifierString(),
                                PurapKeyConstants.ERROR_ITEM_ACCOUNTING_ROUNDING, param1, param2);
                        account.setAmount(calcAmount);
                    }

                    // update percent
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("convertMoneyToPercent() updating percent to " + tmpPercent);
                    }
                    account.setAccountLinePercent(tmpPercent.multiply(new BigDecimal(100)));
                    accountTotalPercent = accountTotalPercent.add(account.getAccountLinePercent());
                    lastAccount = account;

                    // check total based on adjusted amount
                    accountTotal = accountTotal.add(calcAmount);
                    percentTotal = percentTotal.add(tmpPercent);
                }
                BigDecimal percentDifference = new BigDecimal(100).subtract(accountTotalPercent)
                        .setScale(BIG_DECIMAL_SCALE, BigDecimal.ROUND_CEILING);
                if (ObjectUtils.isNotNull(lastAccount)
                        && ObjectUtils.isNotNull(lastAccount.getAccountLinePercent())) {
                    KualiDecimal differencePercent = (((new KualiDecimal(accountTotalPercent))
                            .subtract(new KualiDecimal(100))).abs());
                    if ((differencePercent.abs()).isLessEqual(
                            new KualiDecimal(1).multiply((new KualiDecimal(item.getSourceAccountingLines().size())
                                    .divide(new KualiDecimal(2)))))) {
                        lastAccount
                                .setAccountLinePercent(lastAccount.getAccountLinePercent().add(percentDifference));
                    } else {
                        lastAccount.setAccountLinePercent(lastAccount.getAccountLinePercent());
                    }
                }
            }
        }
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#deleteSummaryAccounts(java.lang.Integer, java.lang.String)
     */
    @Override
    public void deleteSummaryAccounts(Integer purapDocumentIdentifier, String docType) {
        if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) {
            purApAccountingDao.deleteSummaryAccountsbyPaymentRequestIdentifier(purapDocumentIdentifier);
        } else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) {
            purApAccountingDao.deleteSummaryAccountsbyCreditMemoIdentifier(purapDocumentIdentifier);
        } else if (PurapDocTypeCodes.INVOICE_DOCUMENT.equals(docType)) {
            purApAccountingDao.deleteSummaryAccountsbyInvoiceIdentifier(purapDocumentIdentifier);
        }
    }

    @Override
    public List getAccountsPayableSummaryAccounts(Integer purapDocumentIdentifier, String docType) {
        if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) {
            return getSummaryAccountsbyPaymentRequestIdentifier(purapDocumentIdentifier);
        } else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) {
            getSummaryAccountsbyCreditMemoIdentifier(purapDocumentIdentifier);
        } else if (PurapDocTypeCodes.INVOICE_DOCUMENT.equals(docType)) {
            getSummaryAccountsbyInvoiceIdentifier(purapDocumentIdentifier);
        }
        return null;
    }

    @Override
    public List<PurApAccountingLine> getAccountsFromItem(PurApItem item) {
        return purApAccountingDao.getAccountingLinesForItem(item);
    }

    @Override
    public List<SourceAccountingLine> generateSourceAccountsForVendorRemit(
            PurchasingAccountsPayableDocument document) {
        // correct initial amounts or percents
        //updateAccountAmounts(document);
        List<SourceAccountingLine> vendorSummaryAccounts = new ArrayList<SourceAccountingLine>();

        // update accounts here with amounts to send to vendor
        vendorSummaryAccounts = generateSummaryWithNoZeroTotalsNoUseTax(document.getItems());

        return vendorSummaryAccounts;
    }

    /**
     * gets sum total of accounts
     *
     * @param accounts
     * @return
     */

    protected KualiDecimal calculateSumTotal(List<SourceAccountingLine> accounts) {
        KualiDecimal total = KualiDecimal.ZERO;
        for (SourceAccountingLine accountingLine : accounts) {
            KualiDecimal amt = KualiDecimal.ZERO;
            if (ObjectUtils.isNotNull(accountingLine.getAmount())) {
                amt = accountingLine.getAmount();
            }
            total = total.add(amt);
        }
        return total;
    }

    /**
     * Replaces amount field with prorated tax amount in list
     *
     * @param accounts       list of accounts
     * @param useTax         tax to be allocated to these accounts
     * @param newSourceLines rewrites the source account lines
     */

    protected void convertAmtToTax(List<PurApAccountingLine> accounts, KualiDecimal useTax,
            List<SourceAccountingLine> newSourceLines) {
        final BigDecimal HUNDRED = new BigDecimal(100);
        PurApAccountingLine purApAccountingLine;
        BigDecimal proratedAmtBD;
        KualiDecimal proratedAmt;
        // convert back to source
        KualiDecimal total = KualiDecimal.ZERO;
        int last = accounts.size() - 1;
        for (int i = 0; i < last; i++) {
            purApAccountingLine = accounts.get(i);
            BigDecimal linePercent = BigDecimal.ZERO;
            if (ObjectUtils.isNotNull(purApAccountingLine.getAccountLinePercent())) {
                linePercent = purApAccountingLine.getAccountLinePercent();
            }

            proratedAmtBD = useTax.bigDecimalValue().multiply(linePercent);
            // last object takes the rest of the amount
            // proratedAmt = (accounts.indexOf(purApAccountingLine) == last) ? useTax.subtract(total) : proratedAmt.divide(HUNDRED);
            proratedAmtBD = proratedAmtBD.divide(HUNDRED);
            proratedAmt = new KualiDecimal(proratedAmtBD);
            SourceAccountingLine acctLine = purApAccountingLine.generateSourceAccountingLine();
            acctLine.setAmount(proratedAmt);
            newSourceLines.add(acctLine);
            total = total.add(proratedAmt);
        }
        // update last object with remaining balance
        proratedAmt = useTax.subtract(total);
        purApAccountingLine = accounts.get(last);
        SourceAccountingLine acctLine = purApAccountingLine.generateSourceAccountingLine();
        acctLine.setAmount(proratedAmt);
        newSourceLines.add(acctLine);
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#generateUseTaxAccount(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument)
     */
    @Override
    public List<UseTaxContainer> generateUseTaxAccount(PurchasingAccountsPayableDocument document) {
        List<UseTaxContainer> useTaxAccounts = new ArrayList<UseTaxContainer>();

        HashMap<PurApItemUseTax, UseTaxContainer> useTaxItemMap = new HashMap<PurApItemUseTax, UseTaxContainer>();
        Class accountingLineClass = null;
        if (!document.isUseTaxIndicator()) {
            // not useTax, return
            return useTaxAccounts;
        }
        for (PurApItem purApItem : document.getItems()) {
            if (!purApItem.getUseTaxItems().isEmpty()) {
                if (accountingLineClass == null) {
                    accountingLineClass = purApItem.getAccountingLineClass();
                }
                UseTaxContainer useTaxContainer = new UseTaxContainer();
                for (PurApItemUseTax itemUseTax : purApItem.getUseTaxItems()) {
                    if (useTaxItemMap.containsKey(itemUseTax)) {
                        useTaxContainer = useTaxItemMap.get(itemUseTax);
                        PurApItemUseTax exisitingItemUseTax = useTaxContainer.getUseTax();
                        // if already in set we need to add on the old amount
                        KualiDecimal tax = exisitingItemUseTax.getTaxAmount();
                        tax = tax.add(itemUseTax.getTaxAmount());
                        exisitingItemUseTax.setTaxAmount(tax);

                        List<PurApItem> items = useTaxContainer.getItems();
                        items.add(purApItem);
                        useTaxContainer.setItems(items);

                    } else {
                        useTaxContainer = new UseTaxContainer(itemUseTax, purApItem);
                        useTaxItemMap.put(itemUseTax, useTaxContainer);
                        useTaxAccounts.add(useTaxContainer);
                    }
                }
            }
        }
        // iterate over useTaxAccounts and set summary accounts using proration
        for (UseTaxContainer useTaxContainer : useTaxAccounts) {

            // create summary from items
            List<SourceAccountingLine> origSourceAccounts = this
                    .generateSummaryWithNoZeroTotals(useTaxContainer.getItems());
            KualiDecimal totalAmount = calculateSumTotal(origSourceAccounts);
            List<PurApAccountingLine> accountingLines = generateAccountDistributionForProration(origSourceAccounts,
                    totalAmount, PurapConstants.PRORATION_SCALE, accountingLineClass);

            List<SourceAccountingLine> newSourceLines = new ArrayList<SourceAccountingLine>();
            // convert back to source
            convertAmtToTax(accountingLines, useTaxContainer.getUseTax().getTaxAmount(), newSourceLines);

            // do we need an update accounts here?
            useTaxContainer.setAccounts(newSourceLines);
        }

        useTaxAccounts = new ArrayList<UseTaxContainer>(useTaxItemMap.values());
        return useTaxAccounts;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#isTaxAccount(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument,
     *      org.kuali.ole.sys.businessobject.SourceAccountingLine)
     */
    @Override
    public boolean isTaxAccount(PurchasingAccountsPayableDocument document, SourceAccountingLine account) {
        boolean isTaxAccount = false;

        // check if the summary account is for tax withholding
        if (document instanceof PaymentRequestDocument) {
            String incomeClassCode = ((PaymentRequestDocument) document).getTaxClassificationCode();
            if (StringUtils.isNotEmpty(incomeClassCode)) {

                String federalChartCode = parameterService.getParameterValueAsString(PaymentRequestDocument.class,
                        NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX);
                String federalAccountNumber = parameterService.getParameterValueAsString(
                        PaymentRequestDocument.class,
                        NRATaxParameters.FEDERAL_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX);
                String federalObjectCode = parameterService.getSubParameterValueAsString(
                        PaymentRequestDocument.class, NRATaxParameters.FEDERAL_TAX_PARM_PREFIX
                                + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX,
                        incomeClassCode);

                String stateChartCode = parameterService.getParameterValueAsString(PaymentRequestDocument.class,
                        NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_CHART_SUFFIX);
                String stateAccountNumber = parameterService.getParameterValueAsString(PaymentRequestDocument.class,
                        NRATaxParameters.STATE_TAX_PARM_PREFIX + NRATaxParameters.TAX_PARM_ACCOUNT_SUFFIX);
                String stateObjectCode = parameterService.getSubParameterValueAsString(PaymentRequestDocument.class,
                        NRATaxParameters.STATE_TAX_PARM_PREFIX
                                + NRATaxParameters.TAX_PARM_OBJECT_BY_INCOME_CLASS_SUFFIX,
                        incomeClassCode);

                String chartCode = account.getChartOfAccountsCode();
                String accountNumber = account.getAccountNumber();
                String objectCode = account.getFinancialObjectCode();

                boolean isFederalAccount = StringUtils.equals(federalChartCode, chartCode);
                isFederalAccount &= StringUtils.equals(federalAccountNumber, accountNumber);
                isFederalAccount &= StringUtils.equals(federalObjectCode, objectCode);

                boolean isStateAccount = StringUtils.equals(stateChartCode, chartCode);
                isStateAccount &= StringUtils.equals(stateAccountNumber, accountNumber);
                isStateAccount &= StringUtils.equals(stateObjectCode, objectCode);

                isTaxAccount = isFederalAccount || isStateAccount;
            }
        }

        return isTaxAccount;
    }

    public void setParameterService(ParameterService parameterService) {
        this.parameterService = parameterService;
    }

    public void setPurApAccountingDao(PurApAccountingDao purApAccountingDao) {
        this.purApAccountingDao = purApAccountingDao;
    }

    public void setPurapService(PurapService purapService) {
        this.purapService = purapService;
    }

    @Override
    public List<SourceAccountingLine> mergeAccountingLineLists(List<SourceAccountingLine> accountingLines1,
            List<SourceAccountingLine> accountingLines2) {

        KualiDecimal totalAmount = KualiDecimal.ZERO;
        List<SourceAccountingLine> mergedAccountList = new ArrayList();

        for (SourceAccountingLine line1 : accountingLines1) {
            KualiDecimal line1Amount = KualiDecimal.ZERO;
            if (ObjectUtils.isNotNull(line1.getAmount())) {
                line1Amount = line1.getAmount();
            }

            for (SourceAccountingLine line2 : accountingLines2) {
                KualiDecimal line2Amount = KualiDecimal.ZERO;
                if (ObjectUtils.isNotNull(line2.getAmount())) {
                    line2Amount = line2.getAmount();
                }

                // if we find a match between lists, then merge amounts
                if (line1.equals(line2)) {
                    // add the two amounts
                    totalAmount = line1Amount.add(line2Amount);
                    line1.setAmount(totalAmount);
                }
            }

            mergedAccountList.add(line1);
        }

        return mergedAccountList;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyPaymentRequestIdentifier(java.lang.Integer)
     */
    @Override
    public List getSummaryAccountsbyPaymentRequestIdentifier(Integer paymentRequestIdentifier) {
        if (paymentRequestIdentifier != null) {
            Map fieldValues = new HashMap();
            fieldValues.put(PurapPropertyConstants.PAYMENT_REQUEST_ID, paymentRequestIdentifier);
            return new ArrayList(
                    businessObjectService.findMatching(AccountsPayableSummaryAccount.class, fieldValues));
        }
        return null;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyInvoiceIdentifier(java.lang.Integer)
     */
    @Override
    public List getSummaryAccountsbyInvoiceIdentifier(Integer invoiceIdentifier) {
        if (invoiceIdentifier != null) {
            Map fieldValues = new HashMap();
            fieldValues.put(PurapPropertyConstants.INVOICE_ID, invoiceIdentifier);
            return new ArrayList(
                    businessObjectService.findMatching(OleInvoiceAccountsPayableSummaryAccount.class, fieldValues));
        }
        return null;
    }

    /**
     * @see org.kuali.ole.module.purap.service.PurapAccountingService#getSummaryAccountsbyCreditMemoIdentifier(java.lang.Integer)
     */
    @Override
    public List getSummaryAccountsbyCreditMemoIdentifier(Integer creditMemoIdentifier) {
        if (creditMemoIdentifier != null) {
            Map fieldValues = new HashMap();
            fieldValues.put(PurapPropertyConstants.CREDIT_MEMO_ID, creditMemoIdentifier);
            return new ArrayList(
                    businessObjectService.findMatching(AccountsPayableSummaryAccount.class, fieldValues));
        }
        return null;
    }

    /**
     * Sest the businessObjectService.
     *
     * @param businessObjectService
     */
    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }
}