/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.module.purap.service.impl;
import static org.kuali.kfs.module.purap.PurapConstants.HUNDRED;
import static org.kuali.kfs.module.purap.PurapConstants.PURAP_ORIGIN_CODE;
import static org.kuali.kfs.sys.KFSConstants.BALANCE_TYPE_EXTERNAL_ENCUMBRANCE;
import static org.kuali.kfs.sys.KFSConstants.ENCUMB_UPDT_REFERENCE_DOCUMENT_CD;
import static org.kuali.kfs.sys.KFSConstants.GL_CREDIT_CODE;
import static org.kuali.kfs.sys.KFSConstants.GL_DEBIT_CODE;
import static org.kuali.kfs.sys.KFSConstants.MONTH1;
import static org.kuali.rice.core.api.util.type.KualiDecimal.ZERO;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.kuali.kfs.coa.businessobject.ObjectCode;
import org.kuali.kfs.coa.businessobject.SubObjectCode;
import org.kuali.kfs.coa.service.ObjectCodeService;
import org.kuali.kfs.coa.service.SubObjectCodeService;
import org.kuali.kfs.module.purap.PurapConstants;
import org.kuali.kfs.module.purap.PurapConstants.PurapDocTypeCodes;
import org.kuali.kfs.module.purap.businessobject.AccountsPayableSummaryAccount;
import org.kuali.kfs.module.purap.businessobject.CreditMemoItem;
import org.kuali.kfs.module.purap.businessobject.ItemType;
import org.kuali.kfs.module.purap.businessobject.PaymentRequestItem;
import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
import org.kuali.kfs.module.purap.businessobject.PurApItemUseTax;
import org.kuali.kfs.module.purap.businessobject.PurchaseOrderAccount;
import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
import org.kuali.kfs.module.purap.document.AccountsPayableDocument;
import org.kuali.kfs.module.purap.document.PaymentRequestDocument;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
import org.kuali.kfs.module.purap.document.VendorCreditMemoDocument;
import org.kuali.kfs.module.purap.document.service.PaymentRequestService;
import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
import org.kuali.kfs.module.purap.service.PurapAccountRevisionService;
import org.kuali.kfs.module.purap.service.PurapAccountingService;
import org.kuali.kfs.module.purap.service.PurapGeneralLedgerService;
import org.kuali.kfs.module.purap.util.SummaryAccount;
import org.kuali.kfs.module.purap.util.UseTaxContainer;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
import org.kuali.kfs.sys.businessobject.UniversityDate;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.rice.core.api.datetime.DateTimeService;
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.ObjectUtils;
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class PurapGeneralLedgerServiceImpl implements PurapGeneralLedgerService {
private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurapGeneralLedgerServiceImpl.class);
private BusinessObjectService businessObjectService;
private DateTimeService dateTimeService;
private GeneralLedgerPendingEntryService generalLedgerPendingEntryService;
private KualiRuleService kualiRuleService;
private PaymentRequestService paymentRequestService;
private ParameterService parameterService;
private PurapAccountingService purapAccountingService;
private PurchaseOrderService purchaseOrderService;
private UniversityDateService universityDateService;
private ObjectCodeService objectCodeService;
private SubObjectCodeService subObjectCodeService;
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#customizeGeneralLedgerPendingEntry(org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument,
* org.kuali.kfs.sys.businessobject.AccountingLine, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry,
* java.lang.Integer, java.lang.String, java.lang.String, boolean)
*/
@Override
public void customizeGeneralLedgerPendingEntry(PurchasingAccountsPayableDocument purapDocument, AccountingLine accountingLine, GeneralLedgerPendingEntry explicitEntry, Integer referenceDocumentNumber, String debitCreditCode, String docType, boolean isEncumbrance) {
LOG.debug("customizeGeneralLedgerPendingEntry() started");
explicitEntry.setDocumentNumber(purapDocument.getDocumentNumber());
explicitEntry.setTransactionLedgerEntryDescription(entryDescription(purapDocument.getVendorName()));
explicitEntry.setFinancialSystemOriginationCode(PURAP_ORIGIN_CODE);
// Always make the referring document the PO for all PURAP docs except for CM against a vendor.
// This is required for encumbrance entries. It's not required for actual/liability
// entries, but it makes things easier to deal with. If vendor, leave referring stuff blank.
if (ObjectUtils.isNotNull(referenceDocumentNumber)) {
explicitEntry.setReferenceFinancialDocumentNumber(referenceDocumentNumber.toString());
explicitEntry.setReferenceFinancialDocumentTypeCode(PurapDocTypeCodes.PO_DOCUMENT);
explicitEntry.setReferenceFinancialSystemOriginationCode(PURAP_ORIGIN_CODE);
}
// DEFAULT TO USE CURRENT; don't use FY on doc in case it's a prior year
UniversityDate uDate = universityDateService.getCurrentUniversityDate();
explicitEntry.setUniversityFiscalYear(uDate.getUniversityFiscalYear());
explicitEntry.setUniversityFiscalPeriodCode(uDate.getUniversityFiscalAccountingPeriod());
if (PurapDocTypeCodes.PO_DOCUMENT.equals(docType)) {
if (purapDocument.getPostingYear().compareTo(uDate.getUniversityFiscalYear()) > 0) {
// USE NEXT AS SET ON PO; POs can be forward dated to not encumber until next fiscal year
explicitEntry.setUniversityFiscalYear(purapDocument.getPostingYear());
explicitEntry.setUniversityFiscalPeriodCode(MONTH1);
}
}
else if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) {
PaymentRequestDocument preq = (PaymentRequestDocument) purapDocument;
if (paymentRequestService.allowBackpost(preq)) {
LOG.debug("createGlPendingTransaction() within range to allow backpost; posting entry to period 12 of previous FY");
explicitEntry.setUniversityFiscalYear(uDate.getUniversityFiscalYear() - 1);
explicitEntry.setUniversityFiscalPeriodCode(KFSConstants.MONTH12);
}
// if alternate payee is paid for non-primary vendor payment, send alternate vendor name in GL desc
if (preq.getAlternateVendorHeaderGeneratedIdentifier() != null && preq.getAlternateVendorDetailAssignedIdentifier() != null && preq.getVendorHeaderGeneratedIdentifier().compareTo(preq.getAlternateVendorHeaderGeneratedIdentifier()) == 0 && preq.getVendorDetailAssignedIdentifier().compareTo(preq.getAlternateVendorDetailAssignedIdentifier()) == 0) {
explicitEntry.setTransactionLedgerEntryDescription(entryDescription(preq.getPurchaseOrderDocument().getAlternateVendorName()));
}
}
else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) {
VendorCreditMemoDocument cm = (VendorCreditMemoDocument) purapDocument;
if (cm.isSourceDocumentPaymentRequest()) {
// if CM is off of PREQ, use vendor name associated with PREQ (primary or alternate)
PaymentRequestDocument cmPR = cm.getPaymentRequestDocument();
PurchaseOrderDocument cmPO = cm.getPurchaseOrderDocument();
// if alternate payee is paid for non-primary vendor payment, send alternate vendor name in GL desc
if (cmPR.getAlternateVendorHeaderGeneratedIdentifier() != null && cmPR.getAlternateVendorDetailAssignedIdentifier() != null && cmPR.getVendorHeaderGeneratedIdentifier().compareTo(cmPR.getAlternateVendorHeaderGeneratedIdentifier()) == 0 && cmPR.getVendorDetailAssignedIdentifier().compareTo(cmPR.getAlternateVendorDetailAssignedIdentifier()) == 0) {
explicitEntry.setTransactionLedgerEntryDescription(entryDescription(cmPO.getAlternateVendorName()));
}
}
}
else {
throw new IllegalArgumentException("purapDocument (doc #" + purapDocument.getDocumentNumber() + ") is invalid");
}
ObjectCode objectCode = objectCodeService.getByPrimaryId(explicitEntry.getUniversityFiscalYear(), explicitEntry.getChartOfAccountsCode(), explicitEntry.getFinancialObjectCode());
if (ObjectUtils.isNotNull(objectCode)) {
explicitEntry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode());
}
SubObjectCode subObjectCode = subObjectCodeService.getByPrimaryId(explicitEntry.getUniversityFiscalYear(), explicitEntry.getChartOfAccountsCode(), explicitEntry.getAccountNumber(), explicitEntry.getFinancialObjectCode(), explicitEntry.getFinancialSubObjectCode());
if (ObjectUtils.isNotNull(subObjectCode)) {
explicitEntry.setFinancialSubObjectCode(subObjectCode.getFinancialSubObjectCode());
}
if (isEncumbrance) {
explicitEntry.setFinancialBalanceTypeCode(BALANCE_TYPE_EXTERNAL_ENCUMBRANCE);
// D - means the encumbrance is based on the document number
// R - means the encumbrance is based on the referring document number
// All encumbrances should set the update code to 'R' regardless of if they were created by the PO, PREQ, or CM
explicitEntry.setTransactionEncumbranceUpdateCode(ENCUMB_UPDT_REFERENCE_DOCUMENT_CD);
}
// if the amount is negative, flip the D/C indicator
if (accountingLine.getAmount().doubleValue() < 0) {
if (GL_CREDIT_CODE.equals(debitCreditCode)) {
explicitEntry.setTransactionDebitCreditCode(GL_DEBIT_CODE);
}
else {
explicitEntry.setTransactionDebitCreditCode(GL_CREDIT_CODE);
}
}
else {
explicitEntry.setTransactionDebitCreditCode(debitCreditCode);
}
}// end purapCustomizeGeneralLedgerPendingEntry()
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesCancelAccountsPayableDocument(org.kuali.kfs.module.purap.document.AccountsPayableDocument)
*/
@Override
public void generateEntriesCancelAccountsPayableDocument(AccountsPayableDocument apDocument) {
LOG.debug("generateEntriesCancelAccountsPayableDocument() started");
if (apDocument instanceof PaymentRequestDocument) {
LOG.info("generateEntriesCancelAccountsPayableDocument() cancel PaymentRequestDocument");
generateEntriesCancelPaymentRequest((PaymentRequestDocument) apDocument);
}
else if (apDocument instanceof VendorCreditMemoDocument) {
LOG.info("generateEntriesCancelAccountsPayableDocument() cancel CreditMemoDocument");
generateEntriesCancelCreditMemo((VendorCreditMemoDocument) apDocument);
}
else {
// doc not found
}
}
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesCreatePaymentRequest(org.kuali.kfs.module.purap.document.PaymentRequestDocument)
*/
@Override
public void generateEntriesCreatePaymentRequest(PaymentRequestDocument preq) {
LOG.debug("generateEntriesCreatePaymentRequest() started");
List<SourceAccountingLine> encumbrances = relieveEncumbrance(preq);
List<SummaryAccount> summaryAccounts = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq);
generateEntriesPaymentRequest(preq, encumbrances, summaryAccounts, CREATE_PAYMENT_REQUEST);
}
/**
* Called from generateEntriesCancelAccountsPayableDocument() for Payment Request Document
*
* @param preq Payment Request document to cancel
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesCancelAccountsPayableDocument(org.kuali.kfs.module.purap.document.AccountsPayableDocument)
*/
protected void generateEntriesCancelPaymentRequest(PaymentRequestDocument preq) {
LOG.debug("generateEntriesCreatePaymentRequest() started");
List<SourceAccountingLine> encumbrances = reencumberEncumbrance(preq);
List<SummaryAccount> summaryAccounts = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq);
generateEntriesPaymentRequest(preq, encumbrances, summaryAccounts, CANCEL_PAYMENT_REQUEST);
}
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesModifyPaymentRequest(org.kuali.kfs.module.purap.document.PaymentRequestDocument)
*/
@Override
public void generateEntriesModifyPaymentRequest(PaymentRequestDocument preq) {
LOG.debug("generateEntriesModifyPaymentRequest() started");
Map<SourceAccountingLine, KualiDecimal> actualsPositive = new HashMap<SourceAccountingLine, KualiDecimal>();
List<SourceAccountingLine> newAccountingLines = purapAccountingService.generateSummaryWithNoZeroTotalsNoUseTax(preq.getItems());
for (SourceAccountingLine newAccount : newAccountingLines) {
actualsPositive.put(newAccount, newAccount.getAmount());
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesModifyPaymentRequest() actualsPositive: " + newAccount.getAccountNumber() + " = " + newAccount.getAmount());
}
}
Map<SourceAccountingLine, KualiDecimal> actualsNegative = new HashMap<SourceAccountingLine, KualiDecimal>();
List<AccountsPayableSummaryAccount> oldAccountingLines = purapAccountingService.getAccountsPayableSummaryAccounts(preq.getPurapDocumentIdentifier(), PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT);
for (AccountsPayableSummaryAccount oldAccount : oldAccountingLines) {
actualsNegative.put(oldAccount.generateSourceAccountingLine(), oldAccount.getAmount());
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesModifyPaymentRequest() actualsNegative: " + oldAccount.getAccountNumber() + " = " + oldAccount.getAmount());
}
}
// Add the positive entries and subtract the negative entries
Map<SourceAccountingLine, KualiDecimal> glEntries = new HashMap<SourceAccountingLine, KualiDecimal>();
// Combine the two maps (copy all the positive entries)
LOG.debug("generateEntriesModifyPaymentRequest() Combine positive/negative entries");
glEntries.putAll(actualsPositive);
for (Iterator<SourceAccountingLine> iter = actualsNegative.keySet().iterator(); iter.hasNext();) {
SourceAccountingLine key = iter.next();
KualiDecimal amt;
if (glEntries.containsKey(key)) {
amt = glEntries.get(key);
amt = amt.subtract(actualsNegative.get(key));
}
else {
amt = ZERO;
amt = amt.subtract(actualsNegative.get(key));
}
glEntries.put(key, amt);
}
List<SummaryAccount> summaryAccounts = new ArrayList<SummaryAccount>();
for (Iterator<SourceAccountingLine> iter = glEntries.keySet().iterator(); iter.hasNext();) {
SourceAccountingLine account = iter.next();
KualiDecimal amount = glEntries.get(account);
if (ZERO.compareTo(amount) != 0) {
account.setAmount(amount);
SummaryAccount sa = new SummaryAccount(account);
summaryAccounts.add(sa);
}
}
LOG.debug("generateEntriesModifyPaymentRequest() Generate GL entries");
generateEntriesPaymentRequest(preq, null, summaryAccounts, MODIFY_PAYMENT_REQUEST);
}
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesCreateCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument)
*/
@Override
public void generateEntriesCreateCreditMemo(VendorCreditMemoDocument cm) {
LOG.debug("generateEntriesCreateCreditMemo() started");
generateEntriesCreditMemo(cm, CREATE_CREDIT_MEMO);
}
/**
* Called from generateEntriesCancelAccountsPayableDocument() for Payment Request Document
*
* @param preq Payment Request document to cancel
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesCancelAccountsPayableDocument(org.kuali.kfs.module.purap.document.AccountsPayableDocument)
*/
protected void generateEntriesCancelCreditMemo(VendorCreditMemoDocument cm) {
LOG.debug("generateEntriesCancelCreditMemo() started");
generateEntriesCreditMemo(cm, CANCEL_CREDIT_MEMO);
}
/**
* Retrieves the next available sequence number from the general ledger pending entry table for this document
*
* @param documentNumber Document number to find next sequence number
* @return Next available sequence number
*/
protected int getNextAvailableSequence(String documentNumber) {
LOG.debug("getNextAvailableSequence() started");
Map fieldValues = new HashMap();
fieldValues.put("financialSystemOriginationCode", PURAP_ORIGIN_CODE);
fieldValues.put("documentNumber", documentNumber);
int count = businessObjectService.countMatching(GeneralLedgerPendingEntry.class, fieldValues);
return count + 1;
}
/**
* Creates the general ledger entries for Payment Request actions.
*
* @param preq Payment Request document to create entries
* @param encumbrances List of encumbrance accounts if applies
* @param accountingLines List of preq accounts to create entries
* @param processType Type of process (create, modify, cancel)
* @return Boolean returned indicating whether entry creation succeeded
*/
protected boolean generateEntriesPaymentRequest(PaymentRequestDocument preq, List encumbrances, List summaryAccounts, String processType) {
LOG.debug("generateEntriesPaymentRequest() started");
boolean success = true;
preq.setGeneralLedgerPendingEntries(new ArrayList());
/*
* Can't let generalLedgerPendingEntryService just create all the entries because we need the sequenceHelper to carry over
* from the encumbrances to the actuals and also because we need to tell the PaymentRequestDocumentRule customize entry
* method how to customize differently based on if creating an encumbrance or actual.
*/
GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper(getNextAvailableSequence(preq.getDocumentNumber()));
// when cancelling a PREQ, do not book encumbrances if PO is CLOSED
if (encumbrances != null && !(CANCEL_PAYMENT_REQUEST.equals(processType) && PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED.equals(preq.getPurchaseOrderDocument().getApplicationDocumentStatus()))) {
LOG.debug("generateEntriesPaymentRequest() generate encumbrance entries");
if (CREATE_PAYMENT_REQUEST.equals(processType)) {
// on create, use CREDIT code for encumbrances
preq.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
}
else if (CANCEL_PAYMENT_REQUEST.equals(processType)) {
// on cancel, use DEBIT code
preq.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
}
else if (MODIFY_PAYMENT_REQUEST.equals(processType)) {
// no encumbrances for modify
}
preq.setGenerateEncumbranceEntries(true);
for (Iterator iter = encumbrances.iterator(); iter.hasNext();) {
AccountingLine accountingLine = (AccountingLine) iter.next();
preq.generateGeneralLedgerPendingEntries(accountingLine, sequenceHelper);
sequenceHelper.increment(); // increment for the next line
}
}
if (ObjectUtils.isNotNull(summaryAccounts) && !summaryAccounts.isEmpty()) {
LOG.debug("generateEntriesPaymentRequest() now book the actuals");
preq.setGenerateEncumbranceEntries(false);
if (CREATE_PAYMENT_REQUEST.equals(processType) || MODIFY_PAYMENT_REQUEST.equals(processType)) {
// on create and modify, use DEBIT code
preq.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
}
else if (CANCEL_PAYMENT_REQUEST.equals(processType)) {
// on cancel, use CREDIT code
preq.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
}
for (Iterator iter = summaryAccounts.iterator(); iter.hasNext();) {
SummaryAccount summaryAccount = (SummaryAccount) iter.next();
preq.generateGeneralLedgerPendingEntries(summaryAccount.getAccount(), sequenceHelper);
sequenceHelper.increment(); // increment for the next line
}
// generate offset accounts for use tax if it exists (useTaxContainers will be empty if not a use tax document)
List<UseTaxContainer> useTaxContainers = purapAccountingService.generateUseTaxAccount(preq);
for (UseTaxContainer useTaxContainer : useTaxContainers) {
PurApItemUseTax offset = useTaxContainer.getUseTax();
List<SourceAccountingLine> accounts = useTaxContainer.getAccounts();
for (SourceAccountingLine sourceAccountingLine : accounts) {
preq.generateGeneralLedgerPendingEntries(sourceAccountingLine, sequenceHelper, useTaxContainer.getUseTax());
sequenceHelper.increment(); // increment for the next line
}
}
// Manually save preq summary accounts
if (MODIFY_PAYMENT_REQUEST.equals(processType)) {
//for modify, regenerate the summary from the doc
List<SummaryAccount> summaryAccountsForModify = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq);
saveAccountsPayableSummaryAccounts(summaryAccountsForModify, preq.getPurapDocumentIdentifier(), PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT);
}
else {
//for create and cancel, use the summary accounts
saveAccountsPayableSummaryAccounts(summaryAccounts, preq.getPurapDocumentIdentifier(), PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT);
}
// manually save cm account change tables (CAMS needs this)
if (CREATE_PAYMENT_REQUEST.equals(processType) || MODIFY_PAYMENT_REQUEST.equals(processType)) {
SpringContext.getBean(PurapAccountRevisionService.class).savePaymentRequestAccountRevisions(preq.getItems(), preq.getPostingYearFromPendingGLEntries(), preq.getPostingPeriodCodeFromPendingGLEntries());
}
else if (CANCEL_PAYMENT_REQUEST.equals(processType)) {
SpringContext.getBean(PurapAccountRevisionService.class).cancelPaymentRequestAccountRevisions(preq.getItems(), preq.getPostingYearFromPendingGLEntries(), preq.getPostingPeriodCodeFromPendingGLEntries());
}
}
// Manually save GL entries for Payment Request and encumbrances
saveGLEntries(preq.getGeneralLedgerPendingEntries());
return success;
}
/**
* Creates the general ledger entries for Credit Memo actions.
*
* @param cm Credit Memo document to create entries
* @param isCancel Indicates if request is a cancel or create
* @return Boolean returned indicating whether entry creation succeeded
*/
protected boolean generateEntriesCreditMemo(VendorCreditMemoDocument cm, boolean isCancel) {
LOG.debug("generateEntriesCreditMemo() started");
cm.setGeneralLedgerPendingEntries(new ArrayList());
boolean success = true;
GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper(getNextAvailableSequence(cm.getDocumentNumber()));
if (!cm.isSourceVendor()) {
LOG.debug("generateEntriesCreditMemo() create encumbrance entries for CM against a PO or PREQ (not vendor)");
PurchaseOrderDocument po = null;
if (cm.isSourceDocumentPurchaseOrder()) {
LOG.debug("generateEntriesCreditMemo() PO type");
po = purchaseOrderService.getCurrentPurchaseOrder(cm.getPurchaseOrderIdentifier());
}
else if (cm.isSourceDocumentPaymentRequest()) {
LOG.debug("generateEntriesCreditMemo() PREQ type");
po = purchaseOrderService.getCurrentPurchaseOrder(cm.getPaymentRequestDocument().getPurchaseOrderIdentifier());
}
// for CM cancel or create, do not book encumbrances if PO is CLOSED, but do update the amounts on the PO
List encumbrances = getCreditMemoEncumbrance(cm, po, isCancel);
if (!(PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED.equals(po.getApplicationDocumentStatus()))) {
if (encumbrances != null) {
cm.setGenerateEncumbranceEntries(true);
// even if generating encumbrance entries on cancel, call is the same because the method gets negative amounts
// from
// the map so Debits on negatives = a credit
cm.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
for (Iterator iter = encumbrances.iterator(); iter.hasNext();) {
AccountingLine accountingLine = (AccountingLine) iter.next();
if (accountingLine.getAmount().compareTo(ZERO) != 0) {
cm.generateGeneralLedgerPendingEntries(accountingLine, sequenceHelper);
sequenceHelper.increment(); // increment for the next line
}
}
}
}
}
List<SummaryAccount> summaryAccounts = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(cm);
if (summaryAccounts != null) {
LOG.debug("generateEntriesCreditMemo() now book the actuals");
cm.setGenerateEncumbranceEntries(false);
if (!isCancel) {
// on create, use CREDIT code
cm.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
}
else {
// on cancel, use DEBIT code
cm.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
}
for (Iterator iter = summaryAccounts.iterator(); iter.hasNext();) {
SummaryAccount summaryAccount = (SummaryAccount) iter.next();
cm.generateGeneralLedgerPendingEntries(summaryAccount.getAccount(), sequenceHelper);
sequenceHelper.increment(); // increment for the next line
}
// generate offset accounts for use tax if it exists (useTaxContainers will be empty if not a use tax document)
List<UseTaxContainer> useTaxContainers = purapAccountingService.generateUseTaxAccount(cm);
for (UseTaxContainer useTaxContainer : useTaxContainers) {
PurApItemUseTax offset = useTaxContainer.getUseTax();
List<SourceAccountingLine> accounts = useTaxContainer.getAccounts();
for (SourceAccountingLine sourceAccountingLine : accounts) {
cm.generateGeneralLedgerPendingEntries(sourceAccountingLine, sequenceHelper, useTaxContainer.getUseTax());
sequenceHelper.increment(); // increment for the next line
}
}
// manually save cm account change tables (CAMS needs this)
if (!isCancel) {
SpringContext.getBean(PurapAccountRevisionService.class).saveCreditMemoAccountRevisions(cm.getItems(), cm.getPostingYearFromPendingGLEntries(), cm.getPostingPeriodCodeFromPendingGLEntries());
}
else {
SpringContext.getBean(PurapAccountRevisionService.class).cancelCreditMemoAccountRevisions(cm.getItems(), cm.getPostingYearFromPendingGLEntries(), cm.getPostingPeriodCodeFromPendingGLEntries());
}
}
saveGLEntries(cm.getGeneralLedgerPendingEntries());
LOG.debug("generateEntriesCreditMemo() ended");
return success;
}
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesApproveAmendPurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
*/
@Override
public void generateEntriesApproveAmendPurchaseOrder(PurchaseOrderDocument po) {
LOG.debug("generateEntriesApproveAmendPurchaseOrder() started");
// Set outstanding encumbered quantity/amount on items
for (Iterator items = po.getItems().iterator(); items.hasNext();) {
PurchaseOrderItem item = (PurchaseOrderItem) items.next();
// if invoice fields are null (as would be for new items), set fields to zero
item.setItemInvoicedTotalAmount(item.getItemInvoicedTotalAmount() == null ? ZERO : item.getItemInvoicedTotalAmount());
item.setItemInvoicedTotalQuantity(item.getItemInvoicedTotalQuantity() == null ? ZERO : item.getItemInvoicedTotalQuantity());
if (!item.isItemActiveIndicator()) {
// set outstanding encumbrance amounts to zero for inactive items
item.setItemOutstandingEncumberedQuantity(ZERO);
item.setItemOutstandingEncumberedAmount(ZERO);
for (Iterator iter = item.getSourceAccountingLines().iterator(); iter.hasNext();) {
PurchaseOrderAccount account = (PurchaseOrderAccount) iter.next();
account.setItemAccountOutstandingEncumbranceAmount(ZERO);
account.setAlternateAmountForGLEntryCreation(ZERO);
}
}
else {
// Set quantities
if (item.getItemQuantity() != null) {
item.setItemOutstandingEncumberedQuantity(item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity()));
}
else {
// if order qty is null, outstanding encumbered qty should be null
item.setItemOutstandingEncumberedQuantity(null);
}
// Set amount
if (item.getItemOutstandingEncumberedQuantity() != null) {
//do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
KualiDecimal itemEncumber = new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue().multiply(item.getItemUnitPrice()));
//add tax for encumbrance
KualiDecimal itemTaxAmount = item.getItemTaxAmount() == null ? ZERO : item.getItemTaxAmount();
itemEncumber = itemEncumber.add(itemTaxAmount);
item.setItemOutstandingEncumberedAmount(itemEncumber);
}
else {
if (item.getItemUnitPrice() != null) {
item.setItemOutstandingEncumberedAmount(new KualiDecimal(item.getItemUnitPrice().subtract(item.getItemInvoicedTotalAmount().bigDecimalValue())));
}
}
for (Iterator iter = item.getSourceAccountingLines().iterator(); iter.hasNext();) {
PurchaseOrderAccount account = (PurchaseOrderAccount) iter.next();
BigDecimal percent = new BigDecimal(account.getAccountLinePercent().toString());
percent = percent.divide(new BigDecimal("100"), 3, BigDecimal.ROUND_HALF_UP);
account.setItemAccountOutstandingEncumbranceAmount(item.getItemOutstandingEncumberedAmount().multiply(new KualiDecimal(percent)));
account.setAlternateAmountForGLEntryCreation(account.getItemAccountOutstandingEncumbranceAmount());
}
}
}
PurchaseOrderDocument oldPO = purchaseOrderService.getCurrentPurchaseOrder(po.getPurapDocumentIdentifier());
if (oldPO == null) {
throw new IllegalArgumentException("Current Purchase Order not found - poId = " + oldPO.getPurapDocumentIdentifier());
}
List newAccounts = purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly());
List oldAccounts = purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(oldPO.getItemsActiveOnlySetupAlternateAmount());
Map combination = new HashMap();
// Add amounts from the new PO
for (Iterator iter = newAccounts.iterator(); iter.hasNext();) {
SourceAccountingLine newAccount = (SourceAccountingLine) iter.next();
combination.put(newAccount, newAccount.getAmount());
}
LOG.info("generateEntriesApproveAmendPurchaseOrder() combination after the add");
for (Iterator iter = combination.keySet().iterator(); iter.hasNext();) {
SourceAccountingLine element = (SourceAccountingLine) iter.next();
LOG.info("generateEntriesApproveAmendPurchaseOrder() " + element + " = " + ((KualiDecimal) combination.get(element)).floatValue());
}
// Subtract the amounts from the old PO
for (Iterator iter = oldAccounts.iterator(); iter.hasNext();) {
SourceAccountingLine oldAccount = (SourceAccountingLine) iter.next();
if (combination.containsKey(oldAccount)) {
KualiDecimal amount = (KualiDecimal) combination.get(oldAccount);
amount = amount.subtract(oldAccount.getAmount());
combination.put(oldAccount, amount);
}
else {
combination.put(oldAccount, ZERO.subtract(oldAccount.getAmount()));
}
}
LOG.debug("generateEntriesApproveAmendPurchaseOrder() combination after the subtract");
for (Iterator iter = combination.keySet().iterator(); iter.hasNext();) {
SourceAccountingLine element = (SourceAccountingLine) iter.next();
LOG.info("generateEntriesApproveAmendPurchaseOrder() " + element + " = " + ((KualiDecimal) combination.get(element)).floatValue());
}
List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
for (Iterator iter = combination.keySet().iterator(); iter.hasNext();) {
SourceAccountingLine account = (SourceAccountingLine) iter.next();
KualiDecimal amount = (KualiDecimal) combination.get(account);
if (ZERO.compareTo(amount) != 0) {
account.setAmount(amount);
encumbranceAccounts.add(account);
}
}
po.setGlOnlySourceAccountingLines(encumbranceAccounts);
generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
saveGLEntries(po.getGeneralLedgerPendingEntries());
LOG.debug("generateEntriesApproveAmendPo() gl entries created; exit method");
}
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesClosePurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
*/
@Override
public void generateEntriesClosePurchaseOrder(PurchaseOrderDocument po) {
LOG.debug("generateEntriesClosePurchaseOrder() started");
// Set outstanding encumbered quantity/amount on items
for (Iterator items = po.getItems().iterator(); items.hasNext();) {
PurchaseOrderItem item = (PurchaseOrderItem) items.next();
String logItmNbr = "Item # " + item.getItemLineNumber();
if (!item.isItemActiveIndicator()) {
continue;
}
KualiDecimal itemAmount = null;
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesClosePurchaseOrder() " + logItmNbr + " Calculate based on amounts");
}
itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
KualiDecimal accountTotal = ZERO;
PurchaseOrderAccount lastAccount = null;
if (itemAmount.compareTo(ZERO) != 0) {
// Sort accounts
Collections.sort((List) item.getSourceAccountingLines());
for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext();) {
PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
if (!acct.isEmpty()) {
KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
accountTotal = accountTotal.add(acctAmount);
acct.setAlternateAmountForGLEntryCreation(acctAmount);
lastAccount = acct;
}
}
// account for rounding by adjusting last account as needed
if (lastAccount != null) {
KualiDecimal difference = itemAmount.subtract(accountTotal);
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesClosePurchaseOrder() difference: " + logItmNbr + " " + difference);
}
KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
if (ObjectUtils.isNotNull(amount)) {
lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
}
else {
lastAccount.setAlternateAmountForGLEntryCreation(difference);
}
}
}
}// endfor
po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
if (shouldGenerateGLPEForPurchaseOrder(po)) {
generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
saveGLEntries(po.getGeneralLedgerPendingEntries());
LOG.debug("generateEntriesClosePurchaseOrder() gl entries created; exit method");
}
// Set outstanding encumbered quantity/amount on items
for (Iterator items = po.getItems().iterator(); items.hasNext();) {
PurchaseOrderItem item = (PurchaseOrderItem) items.next();
if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
item.setItemOutstandingEncumberedQuantity(KualiDecimal.ZERO);
}
item.setItemOutstandingEncumberedAmount(KualiDecimal.ZERO);
List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
for (PurApAccountingLine purApAccountingLine : sourceAccountingLines) {
PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine;
account.setItemAccountOutstandingEncumbranceAmount(KualiDecimal.ZERO);
}
}
LOG.debug("generateEntriesClosePurchaseOrder() exit method");
}
/**
* We should not generate general ledger pending entries for Purchase Order Close Document and
* Purchase Order Reopen Document with $0 amount.
*
* @param po
* @return
*/
protected boolean shouldGenerateGLPEForPurchaseOrder(PurchaseOrderDocument po) {
for (SourceAccountingLine acct : (List<SourceAccountingLine>)po.getSourceAccountingLines()) {
if (acct.getAmount().abs().compareTo(new KualiDecimal(0)) > 0) {
return true;
}
}
return false;
}
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesReopenPurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
*/
@Override
public void generateEntriesReopenPurchaseOrder(PurchaseOrderDocument po) {
LOG.debug("generateEntriesReopenPurchaseOrder() started");
// Set outstanding encumbered quantity/amount on items
for (Iterator items = po.getItems().iterator(); items.hasNext();) {
PurchaseOrderItem item = (PurchaseOrderItem) items.next();
if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity());
item.setItemOutstandingEncumberedQuantity(item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity()));
item.setItemOutstandingEncumberedAmount(new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue().multiply(item.getItemUnitPrice())));
}
else {
item.setItemOutstandingEncumberedAmount(item.getTotalAmount().subtract(item.getItemInvoicedTotalAmount()));
}
List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
for (PurApAccountingLine purApAccountingLine : sourceAccountingLines) {
PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine;
account.setItemAccountOutstandingEncumbranceAmount(new KualiDecimal(item.getItemOutstandingEncumberedAmount().bigDecimalValue().multiply(account.getAccountLinePercent()).divide(KFSConstants.ONE_HUNDRED.bigDecimalValue())));
}
}// endfor
// Set outstanding encumbered quantity/amount on items
for (Iterator items = po.getItems().iterator(); items.hasNext();) {
PurchaseOrderItem item = (PurchaseOrderItem) items.next();
String logItmNbr = "Item # " + item.getItemLineNumber();
if (!item.isItemActiveIndicator()) {
continue;
}
KualiDecimal itemAmount = null;
if (item.getItemType().isAmountBasedGeneralLedgerIndicator()) {
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesReopenPurchaseOrder() " + logItmNbr + " Calculate based on amounts");
}
itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesReopenPurchaseOrder() " + logItmNbr + " Calculate based on quantities");
}
//do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
itemAmount = new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue().multiply(item.getItemUnitPrice()));
}
KualiDecimal accountTotal = ZERO;
PurchaseOrderAccount lastAccount = null;
if (itemAmount.compareTo(ZERO) != 0) {
// Sort accounts
Collections.sort((List) item.getSourceAccountingLines());
for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext();) {
PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
if (!acct.isEmpty()) {
KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
accountTotal = accountTotal.add(acctAmount);
acct.setAlternateAmountForGLEntryCreation(acctAmount);
lastAccount = acct;
}
}
// account for rounding by adjusting last account as needed
if (lastAccount != null) {
KualiDecimal difference = itemAmount.subtract(accountTotal);
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesReopenPurchaseOrder() difference: " + logItmNbr + " " + difference);
}
KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
if (ObjectUtils.isNotNull(amount)) {
lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
}
else {
lastAccount.setAlternateAmountForGLEntryCreation(difference);
}
}
}
}// endfor
po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
if (shouldGenerateGLPEForPurchaseOrder(po)) {
generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
saveGLEntries(po.getGeneralLedgerPendingEntries());
LOG.debug("generateEntriesReopenPurchaseOrder() gl entries created; exit method");
}
LOG.debug("generateEntriesReopenPurchaseOrder() no gl entries created because the amount is 0; exit method");
}
/**
* @see org.kuali.kfs.module.purap.service.PurapGeneralLedgerService#generateEntriesVoidPurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
*/
@Override
public void generateEntriesVoidPurchaseOrder(PurchaseOrderDocument po) {
LOG.debug("generateEntriesVoidPurchaseOrder() started");
// Set outstanding encumbered quantity/amount on items
for (Iterator items = po.getItems().iterator(); items.hasNext();) {
PurchaseOrderItem item = (PurchaseOrderItem) items.next();
String logItmNbr = "Item # " + item.getItemLineNumber();
if (!item.isItemActiveIndicator()) {
continue;
}
//just use the outstanding amount as recalculating here, particularly the item tax will cause
//amounts to be over or under encumbered and the remaining encumbered amount should be unencumbered during a close
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesVoidPurchaseOrder() " + logItmNbr + " Calculate based on amounts");
}
KualiDecimal itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
KualiDecimal accountTotal = ZERO;
PurchaseOrderAccount lastAccount = null;
if (itemAmount.compareTo(ZERO) != 0) {
// Sort accounts
Collections.sort((List) item.getSourceAccountingLines());
for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext();) {
PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
if (!acct.isEmpty()) {
KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
accountTotal = accountTotal.add(acctAmount);
acct.setAlternateAmountForGLEntryCreation(acctAmount);
lastAccount = acct;
}
}
// account for rounding by adjusting last account as needed
if (lastAccount != null) {
KualiDecimal difference = itemAmount.subtract(accountTotal);
if (LOG.isDebugEnabled()) {
LOG.debug("generateEntriesVoidPurchaseOrder() difference: " + logItmNbr + " " + difference);
}
KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
if (ObjectUtils.isNotNull(amount)) {
lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
}
else {
lastAccount.setAlternateAmountForGLEntryCreation(difference);
}
}
}
}// endfor
po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
saveGLEntries(po.getGeneralLedgerPendingEntries());
LOG.debug("generateEntriesVoidPurchaseOrder() gl entries created; exit method");
}
/**
* Relieve the Encumbrance on a PO based on values in a PREQ. This is to be called when a PREQ is created. Note: This modifies
* the encumbrance values on the PO and saves the PO
*
* @param preq PREQ for invoice
* @return List of accounting lines to use to create the pending general ledger entries
*/
protected List<SourceAccountingLine> relieveEncumbrance(PaymentRequestDocument preq) {
LOG.debug("relieveEncumbrance() started");
Map encumbranceAccountMap = new HashMap();
PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(preq.getPurchaseOrderIdentifier());
// Get each item one by one
for (Iterator items = preq.getItems().iterator(); items.hasNext();) {
PaymentRequestItem preqItem = (PaymentRequestItem) items.next();
PurchaseOrderItem poItem = getPoItem(po, preqItem.getItemLineNumber(), preqItem.getItemType());
boolean takeAll = false; // Set this true if we relieve the entire encumbrance
KualiDecimal itemDisEncumber = null; // Amount to disencumber for this item
String logItmNbr = "Item # " + preqItem.getItemLineNumber();
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr);
}
// If there isn't a PO item or the extended price is 0, we don't need encumbrances
if (poItem == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " No encumbrances required because po item is null");
}
}
else {
final KualiDecimal preqItemTotalAmount = (preqItem.getTotalAmount() == null) ? KualiDecimal.ZERO : preqItem.getTotalAmount();
if (ZERO.compareTo(preqItemTotalAmount) == 0) {
/*
* This is a specialized case where PREQ item being processed must adjust the PO item's outstanding encumbered
* quantity. This kind of scenario is mostly seen on warranty type items. The following must be true to do this:
* PREQ item Extended Price must be ZERO, PREQ item invoice quantity must be not empty and not ZERO, and PO item
* is quantity based PO item unit cost is ZERO
*/
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " No GL encumbrances required because extended price is ZERO");
}
if ((poItem.getItemQuantity() != null) && ((BigDecimal.ZERO.compareTo(poItem.getItemUnitPrice())) == 0)) {
// po has order quantity and unit price is ZERO... reduce outstanding encumbered quantity
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate po oustanding encumbrance");
}
// Do encumbrance calculations based on quantity
if ((preqItem.getItemQuantity() != null) && ((ZERO.compareTo(preqItem.getItemQuantity())) != 0)) {
KualiDecimal invoiceQuantity = preqItem.getItemQuantity();
KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
KualiDecimal encumbranceQuantity;
if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
// We bought more than the quantity on the PO
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
}
encumbranceQuantity = outstandingEncumberedQuantity;
poItem.setItemOutstandingEncumberedQuantity(ZERO);
}
else {
encumbranceQuantity = invoiceQuantity;
poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " adjusting oustanding encunbrance qty - encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
}
}
if (poItem.getItemInvoicedTotalQuantity() == null) {
poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
}
else {
poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
}
}
}
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
}
// Do we calculate the encumbrance amount based on quantity or amount?
if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
}
// Do encumbrance calculations based on quantity
KualiDecimal invoiceQuantity = preqItem.getItemQuantity() == null ? ZERO : preqItem.getItemQuantity();
KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
KualiDecimal encumbranceQuantity;
if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
// We bought more than the quantity on the PO
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
}
encumbranceQuantity = outstandingEncumberedQuantity;
poItem.setItemOutstandingEncumberedQuantity(ZERO);
takeAll = true;
}
else {
encumbranceQuantity = invoiceQuantity;
poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
if (ZERO.compareTo(poItem.getItemOutstandingEncumberedQuantity()) == 0) {
takeAll = true;
}
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
}
}
if (poItem.getItemInvoicedTotalQuantity() == null) {
poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
}
else {
poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
}
itemDisEncumber = new KualiDecimal(encumbranceQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
//add tax for encumbrance
KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
KualiDecimal encumbranceTaxAmount = encumbranceQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount);
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
}
// Do encumbrance calculations based on amount only
if ((poItem.getItemOutstandingEncumberedAmount().bigDecimalValue().signum() == -1) && (preqItemTotalAmount.bigDecimalValue().signum() == -1)) {
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is negative: " + poItem.getItemOutstandingEncumberedAmount());
}
if (preqItemTotalAmount.compareTo(poItem.getItemOutstandingEncumberedAmount()) >= 0) {
// extended price is equal to or greater than outstanding encumbered
itemDisEncumber = preqItemTotalAmount;
}
else {
// extended price is less than outstanding encumbered
takeAll = true;
itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
}
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is positive or ZERO: " + poItem.getItemOutstandingEncumberedAmount());
}
if (poItem.getItemOutstandingEncumberedAmount().compareTo(preqItemTotalAmount) >= 0) {
// outstanding amount is equal to or greater than extended price
itemDisEncumber = preqItemTotalAmount;
}
else {
// outstanding amount is less than extended price
takeAll = true;
itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " Amount to disencumber: " + itemDisEncumber);
}
KualiDecimal newOutstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount().subtract(itemDisEncumber);
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " New Outstanding Encumbered amount is : " + newOutstandingEncumberedAmount);
}
poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
KualiDecimal newInvoicedTotalAmount = poItem.getItemInvoicedTotalAmount().add(preqItemTotalAmount);
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " New Invoiced Total Amount is: " + newInvoicedTotalAmount);
}
poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
// Sort accounts
Collections.sort((List) poItem.getSourceAccountingLines());
// make the list of accounts for the disencumbrance entry
PurchaseOrderAccount lastAccount = null;
KualiDecimal accountTotal = ZERO;
for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext();) {
PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
if (!account.isEmpty()) {
KualiDecimal encumbranceAmount = null;
SourceAccountingLine acctString = account.generateSourceAccountingLine();
if (takeAll) {
// fully paid; remove remaining encumbrance
encumbranceAmount = account.getItemAccountOutstandingEncumbranceAmount();
account.setItemAccountOutstandingEncumbranceAmount(ZERO);
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " take all");
}
}
else {
// amount = item disencumber * account percent / 100
encumbranceAmount = itemDisEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().subtract(encumbranceAmount));
// For rounding check at the end
accountTotal = accountTotal.add(encumbranceAmount);
// If we are zeroing out the encumbrance, we don't need to adjust for rounding
if (!takeAll) {
lastAccount = account;
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() " + logItmNbr + " " + acctString + " = " + encumbranceAmount);
}
if (ObjectUtils.isNull(encumbranceAccountMap.get(acctString))) {
encumbranceAccountMap.put(acctString, encumbranceAmount);
}
else {
KualiDecimal amt = (KualiDecimal) encumbranceAccountMap.get(acctString);
encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount));
}
}
}
// account for rounding by adjusting last account as needed
if (lastAccount != null) {
KualiDecimal difference = itemDisEncumber.subtract(accountTotal);
if (LOG.isDebugEnabled()) {
LOG.debug("relieveEncumbrance() difference: " + logItmNbr + " " + difference);
}
SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
if (ObjectUtils.isNull(amount)) {
encumbranceAccountMap.put(acctString, difference);
}
else {
encumbranceAccountMap.put(acctString, amount.add(difference));
}
lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().subtract(difference));
}
}
}
}// endfor
List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
for (Iterator iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext();) {
SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
if (amount.doubleValue() != 0) {
acctString.setAmount(amount);
encumbranceAccounts.add(acctString);
}
}
SpringContext.getBean(BusinessObjectService.class).save(po);
return encumbranceAccounts;
}
/**
* Re-encumber the Encumbrance on a PO based on values in a PREQ. This is used when a PREQ is cancelled. Note: This modifies the
* encumbrance values on the PO and saves the PO
*
* @param preq PREQ for invoice
* @return List of accounting lines to use to create the pending general ledger entries
*/
protected List<SourceAccountingLine> reencumberEncumbrance(PaymentRequestDocument preq) {
LOG.debug("reencumberEncumbrance() started");
PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(preq.getPurchaseOrderIdentifier());
Map encumbranceAccountMap = new HashMap();
// Get each item one by one
for (Iterator items = preq.getItems().iterator(); items.hasNext();) {
PaymentRequestItem payRequestItem = (PaymentRequestItem) items.next();
PurchaseOrderItem poItem = getPoItem(po, payRequestItem.getItemLineNumber(), payRequestItem.getItemType());
KualiDecimal itemReEncumber = null; // Amount to reencumber for this item
String logItmNbr = "Item # " + payRequestItem.getItemLineNumber();
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr);
}
// If there isn't a PO item or the total amount is 0, we don't need encumbrances
final KualiDecimal preqItemTotalAmount = (payRequestItem.getTotalAmount() == null) ? KualiDecimal.ZERO : payRequestItem.getTotalAmount();
if ((poItem == null) || (preqItemTotalAmount.doubleValue() == 0)) {
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " No encumbrances required");
}
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
}
// Do we calculate the encumbrance amount based on quantity or amount?
if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
}
// Do disencumbrance calculations based on quantity
KualiDecimal preqQuantity = payRequestItem.getItemQuantity() == null ? ZERO : payRequestItem.getItemQuantity();
KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
KualiDecimal invoicedTotal = poItem.getItemInvoicedTotalQuantity() == null ? ZERO : poItem.getItemInvoicedTotalQuantity();
poItem.setItemInvoicedTotalQuantity(invoicedTotal.subtract(preqQuantity));
poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.add(preqQuantity));
//do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
itemReEncumber = new KualiDecimal(preqQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
//add tax for encumbrance
KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
KualiDecimal encumbranceTaxAmount = preqQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
itemReEncumber = itemReEncumber.add(encumbranceTaxAmount);
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
}
itemReEncumber = preqItemTotalAmount;
// if re-encumber amount is more than original PO ordered amount... do not exceed ordered amount
// this prevents negative encumbrance
if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() < 0)) {
// po item extended cost is negative
if ((poItem.getTotalAmount().compareTo(itemReEncumber)) > 0) {
itemReEncumber = poItem.getTotalAmount();
}
}
else if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() >= 0)) {
// po item extended cost is positive
if ((poItem.getTotalAmount().compareTo(itemReEncumber)) < 0) {
itemReEncumber = poItem.getTotalAmount();
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " Amount to reencumber: " + itemReEncumber);
}
KualiDecimal outstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount() == null ? ZERO : poItem.getItemOutstandingEncumberedAmount();
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Outstanding Encumbrance Amount set to: " + outstandingEncumberedAmount);
}
KualiDecimal newOutstandingEncumberedAmount = outstandingEncumberedAmount.add(itemReEncumber);
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Outstanding Encumbrance Amount to set: " + newOutstandingEncumberedAmount);
}
poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
KualiDecimal invoicedTotalAmount = poItem.getItemInvoicedTotalAmount() == null ? ZERO : poItem.getItemInvoicedTotalAmount();
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Invoiced Total Amount set to: " + invoicedTotalAmount);
}
KualiDecimal newInvoicedTotalAmount = invoicedTotalAmount.subtract(preqItemTotalAmount);
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Invoiced Total Amount to set: " + newInvoicedTotalAmount);
}
poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
// make the list of accounts for the reencumbrance entry
PurchaseOrderAccount lastAccount = null;
KualiDecimal accountTotal = ZERO;
// Sort accounts
Collections.sort((List) poItem.getSourceAccountingLines());
for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext();) {
PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
if (!account.isEmpty()) {
SourceAccountingLine acctString = account.generateSourceAccountingLine();
// amount = item reencumber * account percent / 100
KualiDecimal reencumbranceAmount = itemReEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().add(reencumbranceAmount));
// For rounding check at the end
accountTotal = accountTotal.add(reencumbranceAmount);
lastAccount = account;
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() " + logItmNbr + " " + acctString + " = " + reencumbranceAmount);
}
if (encumbranceAccountMap.containsKey(acctString)) {
KualiDecimal currentAmount = (KualiDecimal) encumbranceAccountMap.get(acctString);
encumbranceAccountMap.put(acctString, reencumbranceAmount.add(currentAmount));
}
else {
encumbranceAccountMap.put(acctString, reencumbranceAmount);
}
}
}
// account for rounding by adjusting last account as needed
if (lastAccount != null) {
KualiDecimal difference = itemReEncumber.subtract(accountTotal);
if (LOG.isDebugEnabled()) {
LOG.debug("reencumberEncumbrance() difference: " + logItmNbr + " " + difference);
}
SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
if (amount == null) {
encumbranceAccountMap.put(acctString, difference);
}
else {
encumbranceAccountMap.put(acctString, amount.add(difference));
}
lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference));
}
}
}
SpringContext.getBean(BusinessObjectService.class).save(po);
List<SourceAccountingLine> encumbranceAccounts = new ArrayList<SourceAccountingLine>();
for (Iterator<SourceAccountingLine> iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext();) {
SourceAccountingLine acctString = iter.next();
KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
if (amount.doubleValue() != 0) {
acctString.setAmount(amount);
encumbranceAccounts.add(acctString);
}
}
return encumbranceAccounts;
}
/**
* Re-encumber the Encumbrance on a PO based on values in a PREQ. This is used when a PREQ is cancelled. Note: This modifies the
* encumbrance values on the PO and saves the PO
*
* @param cm Credit Memo document
* @param po Purchase Order document modify encumbrances
* @return List of accounting lines to use to create the pending general ledger entries
*/
protected List<SourceAccountingLine> getCreditMemoEncumbrance(VendorCreditMemoDocument cm, PurchaseOrderDocument po, boolean cancel) {
LOG.debug("getCreditMemoEncumbrance() started");
if (ObjectUtils.isNull(po)) {
return null;
}
if (cancel) {
LOG.debug("getCreditMemoEncumbrance() Receiving items back from vendor (cancelled CM)");
}
else {
LOG.debug("getCreditMemoEncumbrance() Returning items to vendor");
}
Map encumbranceAccountMap = new HashMap();
// Get each item one by one
for (Iterator items = cm.getItems().iterator(); items.hasNext();) {
CreditMemoItem cmItem = (CreditMemoItem) items.next();
PurchaseOrderItem poItem = getPoItem(po, cmItem.getItemLineNumber(), cmItem.getItemType());
KualiDecimal itemDisEncumber = null; // Amount to disencumber for this item
KualiDecimal itemAlterInvoiceAmt = null; // Amount to alter the invoicedAmt on the PO item
String logItmNbr = "Item # " + cmItem.getItemLineNumber();
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() " + logItmNbr);
}
final KualiDecimal cmItemTotalAmount = (cmItem.getTotalAmount() == null) ? KualiDecimal.ZERO : cmItem.getTotalAmount();
;
// If there isn't a PO item or the total amount is 0, we don't need encumbrances
if ((poItem == null) || (cmItemTotalAmount == null) || (cmItemTotalAmount.doubleValue() == 0)) {
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " No encumbrances required");
}
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
}
// Do we calculate the encumbrance amount based on quantity or amount?
if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
}
// Do encumbrance calculations based on quantity
KualiDecimal cmQuantity = cmItem.getItemQuantity() == null ? ZERO : cmItem.getItemQuantity();
KualiDecimal encumbranceQuantityChange = calculateQuantityChange(cancel, poItem, cmQuantity);
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " encumbranceQtyChange " + encumbranceQuantityChange + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity() + " invoicedTotalQuantity " + poItem.getItemInvoicedTotalQuantity());
}
//do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
itemDisEncumber = new KualiDecimal(encumbranceQuantityChange.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
//add tax for encumbrance
KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
KualiDecimal encumbranceTaxAmount = encumbranceQuantityChange.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount);
itemAlterInvoiceAmt = cmItemTotalAmount;
if (cancel) {
itemAlterInvoiceAmt = itemAlterInvoiceAmt.multiply(new KualiDecimal("-1"));
}
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
}
// Do encumbrance calculations based on amount only
if (cancel) {
// Decrease encumbrance
itemDisEncumber = cmItemTotalAmount.multiply(new KualiDecimal("-1"));
if (poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber).doubleValue() < 0) {
LOG.debug("getCreditMemoEncumbrance() Cancel overflow");
itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
}
}
else {
// Increase encumbrance
itemDisEncumber = cmItemTotalAmount;
if (poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber).doubleValue() > poItem.getTotalAmount().doubleValue()) {
LOG.debug("getCreditMemoEncumbrance() Create overflow");
itemDisEncumber = poItem.getTotalAmount().subtract(poItem.getItemOutstandingEncumberedAmount());
}
}
itemAlterInvoiceAmt = itemDisEncumber;
}
// alter the encumbrance based on what was originally encumbered
poItem.setItemOutstandingEncumberedAmount(poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber));
// alter the invoiced amt based on what was actually credited on the credit memo
poItem.setItemInvoicedTotalAmount(poItem.getItemInvoicedTotalAmount().subtract(itemAlterInvoiceAmt));
if (poItem.getItemInvoicedTotalAmount().compareTo(ZERO) < 0) {
poItem.setItemInvoicedTotalAmount(ZERO);
}
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Amount to disencumber: " + itemDisEncumber);
}
// Sort accounts
Collections.sort((List) poItem.getSourceAccountingLines());
// make the list of accounts for the disencumbrance entry
PurchaseOrderAccount lastAccount = null;
KualiDecimal accountTotal = ZERO;
// Collections.sort((List)poItem.getSourceAccountingLines());
for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext();) {
PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
if (!account.isEmpty()) {
KualiDecimal encumbranceAmount = null;
SourceAccountingLine acctString = account.generateSourceAccountingLine();
// amount = item disencumber * account percent / 100
encumbranceAmount = itemDisEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(new KualiDecimal(100));
account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().add(encumbranceAmount));
// For rounding check at the end
accountTotal = accountTotal.add(encumbranceAmount);
lastAccount = account;
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " " + acctString + " = " + encumbranceAmount);
}
if (encumbranceAccountMap.get(acctString) == null) {
encumbranceAccountMap.put(acctString, encumbranceAmount);
}
else {
KualiDecimal amt = (KualiDecimal) encumbranceAccountMap.get(acctString);
encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount));
}
}
}
// account for rounding by adjusting last account as needed
if (lastAccount != null) {
KualiDecimal difference = itemDisEncumber.subtract(accountTotal);
if (LOG.isDebugEnabled()) {
LOG.debug("getCreditMemoEncumbrance() difference: " + logItmNbr + " " + difference);
}
SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
if (amount == null) {
encumbranceAccountMap.put(acctString, difference);
}
else {
encumbranceAccountMap.put(acctString, amount.add(difference));
}
lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference));
}
}
}
List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
for (Iterator iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext();) {
SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
if (amount.doubleValue() != 0) {
acctString.setAmount(amount);
encumbranceAccounts.add(acctString);
}
}
SpringContext.getBean(BusinessObjectService.class).save(po);
return encumbranceAccounts;
}
/**
* Save the given general ledger entries
*
* @param glEntries List of GeneralLedgerPendingEntries to be saved
*/
protected void saveGLEntries(List<GeneralLedgerPendingEntry> glEntries) {
LOG.debug("saveGLEntries() started");
businessObjectService.save(glEntries);
}
/**
* Save the given accounts for the given document.
*
* @param sourceLines Accounts to be saved
* @param purapDocumentIdentifier Purap document id for accounts
*/
protected void saveAccountsPayableSummaryAccounts(List<SummaryAccount> summaryAccounts, Integer purapDocumentIdentifier, String docType) {
LOG.debug("saveAccountsPayableSummaryAccounts() started");
purapAccountingService.deleteSummaryAccounts(purapDocumentIdentifier, docType);
List<AccountsPayableSummaryAccount> apSummaryAccounts = new ArrayList();
for (SummaryAccount summaryAccount : summaryAccounts) {
apSummaryAccounts.add(new AccountsPayableSummaryAccount(summaryAccount.getAccount(), purapDocumentIdentifier, docType));
}
businessObjectService.save(apSummaryAccounts);
}
/**
* Find item in PO based on given parameters. Must send either the line # or item type.
*
* @param po Purchase Order containing list of items
* @param nbr Line # of desired item (could be null)
* @param itemType Item type of desired item
* @return PurcahseOrderItem found matching given criteria
*/
protected PurchaseOrderItem getPoItem(PurchaseOrderDocument po, Integer nbr, ItemType itemType) {
LOG.debug("getPoItem() started");
for (Iterator iter = po.getItems().iterator(); iter.hasNext();) {
PurchaseOrderItem element = (PurchaseOrderItem) iter.next();
if (itemType.isLineItemIndicator()) {
if (ObjectUtils.isNotNull(nbr) && ObjectUtils.isNotNull(element.getItemLineNumber()) && (nbr.compareTo(element.getItemLineNumber()) == 0)) {
return element;
}
}
else {
if (element.getItemTypeCode().equals(itemType.getItemTypeCode())) {
return element;
}
}
}
return null;
}
/**
* Format description for general ledger entry. Currently making sure length is less than 40 char.
*
* @param description String to be formatted
* @return Formatted String
*/
protected String entryDescription(String description) {
if (description != null && description.length() > 40) {
return description.toString().substring(0, 39);
}
else {
return description;
}
}
/**
* Calculate quantity change for creating Credit Memo entries
*
* @param cancel Boolean indicating whether entries are for creation or cancellation of credit memo
* @param poItem Purchase Order Item
* @param cmQuantity Quantity on credit memo item
* @return Calculated change
*/
protected KualiDecimal calculateQuantityChange(boolean cancel, PurchaseOrderItem poItem, KualiDecimal cmQuantity) {
LOG.debug("calculateQuantityChange() started");
// Calculate quantity change & adjust invoiced quantity & outstanding encumbered quantity
KualiDecimal encumbranceQuantityChange = null;
if (cancel) {
encumbranceQuantityChange = cmQuantity.multiply(new KualiDecimal("-1"));
}
else {
encumbranceQuantityChange = cmQuantity;
}
poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().subtract(encumbranceQuantityChange));
poItem.setItemOutstandingEncumberedQuantity(poItem.getItemOutstandingEncumberedQuantity().add(encumbranceQuantityChange));
// Check for overflows
if (cancel) {
if (poItem.getItemOutstandingEncumberedQuantity().doubleValue() < 0) {
LOG.debug("calculateQuantityChange() Cancel overflow");
KualiDecimal difference = poItem.getItemOutstandingEncumberedQuantity().abs();
poItem.setItemOutstandingEncumberedQuantity(ZERO);
poItem.setItemInvoicedTotalQuantity(poItem.getItemQuantity());
encumbranceQuantityChange = encumbranceQuantityChange.add(difference);
}
}
else {
if (poItem.getItemInvoicedTotalQuantity().doubleValue() < 0) {
LOG.debug("calculateQuantityChange() Create overflow");
KualiDecimal difference = poItem.getItemInvoicedTotalQuantity().abs();
poItem.setItemOutstandingEncumberedQuantity(poItem.getItemQuantity());
poItem.setItemInvoicedTotalQuantity(ZERO);
encumbranceQuantityChange = encumbranceQuantityChange.add(difference);
}
}
return encumbranceQuantityChange;
}
public void setDateTimeService(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
public void setBusinessObjectService(BusinessObjectService businessObjectService) {
this.businessObjectService = businessObjectService;
}
public void setGeneralLedgerPendingEntryService(GeneralLedgerPendingEntryService generalLedgerPendingEntryService) {
this.generalLedgerPendingEntryService = generalLedgerPendingEntryService;
}
public void setKualiRuleService(KualiRuleService kualiRuleService) {
this.kualiRuleService = kualiRuleService;
}
public void setPurapAccountingService(PurapAccountingService purapAccountingService) {
this.purapAccountingService = purapAccountingService;
}
public void setUniversityDateService(UniversityDateService universityDateService) {
this.universityDateService = universityDateService;
}
public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
this.purchaseOrderService = purchaseOrderService;
}
public void setObjectCodeService(ObjectCodeService objectCodeService) {
this.objectCodeService = objectCodeService;
}
public void setSubObjectCodeService(SubObjectCodeService subObjectCodeService) {
this.subObjectCodeService = subObjectCodeService;
}
public void setParameterService(ParameterService parameterService) {
this.parameterService = parameterService;
}
public void setPaymentRequestService(PaymentRequestService paymentRequestService) {
this.paymentRequestService = paymentRequestService;
}
}