001/*
002 * Copyright 2007 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.ole.module.purap.service.impl;
017
018import org.kuali.ole.coa.businessobject.Account;
019import org.kuali.ole.coa.businessobject.ObjectCode;
020import org.kuali.ole.coa.businessobject.SubObjectCode;
021import org.kuali.ole.coa.service.ObjectCodeService;
022import org.kuali.ole.coa.service.SubObjectCodeService;
023import org.kuali.ole.module.purap.PurapConstants;
024import org.kuali.ole.module.purap.PurapConstants.PurapDocTypeCodes;
025import org.kuali.ole.module.purap.businessobject.*;
026import org.kuali.ole.module.purap.document.*;
027import org.kuali.ole.module.purap.document.InvoiceDocument;
028import org.kuali.ole.module.purap.document.service.InvoiceService;
029import org.kuali.ole.module.purap.document.service.PaymentRequestService;
030import org.kuali.ole.module.purap.document.service.PurapService;
031import org.kuali.ole.module.purap.document.service.PurchaseOrderService;
032import org.kuali.ole.module.purap.service.PurapAccountRevisionService;
033import org.kuali.ole.module.purap.service.PurapAccountingService;
034import org.kuali.ole.module.purap.service.PurapGeneralLedgerService;
035import org.kuali.ole.module.purap.util.SummaryAccount;
036import org.kuali.ole.module.purap.util.UseTaxContainer;
037import org.kuali.ole.select.businessobject.OleInvoiceAccountsPayableSummaryAccount;
038import org.kuali.ole.select.businessobject.OleInvoiceItem;
039import org.kuali.ole.select.businessobject.OlePaymentRequestItem;
040import org.kuali.ole.sys.OLEConstants;
041import org.kuali.ole.sys.businessobject.*;
042import org.kuali.ole.sys.context.SpringContext;
043import org.kuali.ole.sys.service.GeneralLedgerPendingEntryService;
044import org.kuali.ole.sys.service.UniversityDateService;
045import org.kuali.rice.core.api.datetime.DateTimeService;
046import org.kuali.rice.core.api.util.type.KualiDecimal;
047import org.kuali.rice.coreservice.framework.parameter.ParameterService;
048import org.kuali.rice.krad.service.BusinessObjectService;
049import org.kuali.rice.krad.service.KualiRuleService;
050import org.kuali.rice.krad.util.ObjectUtils;
051import org.springframework.transaction.annotation.Transactional;
052
053import java.math.BigDecimal;
054import java.util.*;
055
056import static org.kuali.ole.module.purap.PurapConstants.HUNDRED;
057import static org.kuali.ole.module.purap.PurapConstants.PURAP_ORIGIN_CODE;
058import static org.kuali.ole.sys.OLEConstants.*;
059import static org.kuali.rice.core.api.util.type.KualiDecimal.ZERO;
060
061@Transactional
062public class PurapGeneralLedgerServiceImpl implements PurapGeneralLedgerService {
063    private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurapGeneralLedgerServiceImpl.class);
064
065    private BusinessObjectService businessObjectService;
066    private DateTimeService dateTimeService;
067    private GeneralLedgerPendingEntryService generalLedgerPendingEntryService;
068    private KualiRuleService kualiRuleService;
069    private PaymentRequestService paymentRequestService;
070    private ParameterService parameterService;
071    private PurapAccountingService purapAccountingService;
072    private PurchaseOrderService purchaseOrderService;
073    private UniversityDateService universityDateService;
074    private ObjectCodeService objectCodeService;
075    private SubObjectCodeService subObjectCodeService;
076    private InvoiceService invoiceService;
077
078    /**
079     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#customizeGeneralLedgerPendingEntry(org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument,
080     *      org.kuali.ole.sys.businessobject.AccountingLine, org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry,
081     *      java.lang.Integer, java.lang.String, java.lang.String, boolean)
082     */
083    public void customizeGeneralLedgerPendingEntry(PurchasingAccountsPayableDocument purapDocument, AccountingLine accountingLine, GeneralLedgerPendingEntry explicitEntry, Integer referenceDocumentNumber, String debitCreditCode, String docType, boolean isEncumbrance) {
084        LOG.debug("customizeGeneralLedgerPendingEntry() started");
085
086        explicitEntry.setDocumentNumber(purapDocument.getDocumentNumber());
087        explicitEntry.setTransactionLedgerEntryDescription(entryDescription(purapDocument.getVendorName()));
088        explicitEntry.setFinancialSystemOriginationCode(PURAP_ORIGIN_CODE);
089
090        // Always make the referring document the PO for all PURAP docs except for CM against a vendor.
091        // This is required for encumbrance entries. It's not required for actual/liability
092        // entries, but it makes things easier to deal with. If vendor, leave referring stuff blank.
093        if (ObjectUtils.isNotNull(referenceDocumentNumber)) {
094            explicitEntry.setReferenceFinancialDocumentNumber(referenceDocumentNumber.toString());
095            explicitEntry.setReferenceFinancialDocumentTypeCode(PurapDocTypeCodes.PO_DOCUMENT);
096            explicitEntry.setReferenceFinancialSystemOriginationCode(PURAP_ORIGIN_CODE);
097        }
098
099        // DEFAULT TO USE CURRENT; don't use FY on doc in case it's a prior year
100        UniversityDate uDate = universityDateService.getCurrentUniversityDate();
101        explicitEntry.setUniversityFiscalYear(uDate.getUniversityFiscalYear());
102        explicitEntry.setUniversityFiscalPeriodCode(uDate.getUniversityFiscalAccountingPeriod());
103
104        if (PurapDocTypeCodes.PO_DOCUMENT.equals(docType)) {
105            if (purapDocument.getPostingYear().compareTo(uDate.getUniversityFiscalYear()) > 0) {
106                // USE NEXT AS SET ON PO; POs can be forward dated to not encumber until next fiscal year
107                explicitEntry.setUniversityFiscalYear(purapDocument.getPostingYear());
108                explicitEntry.setUniversityFiscalPeriodCode(MONTH1);
109            }
110        } else if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) {
111            PaymentRequestDocument preq = (PaymentRequestDocument) purapDocument;
112            if (paymentRequestService.allowBackpost(preq)) {
113                LOG.debug("createGlPendingTransaction() within range to allow backpost; posting entry to period 12 of previous FY");
114                explicitEntry.setUniversityFiscalYear(uDate.getUniversityFiscalYear() - 1);
115                explicitEntry.setUniversityFiscalPeriodCode(OLEConstants.MONTH12);
116            }
117
118            // if alternate payee is paid for non-primary vendor payment, send alternate vendor name in GL desc
119            if (preq.getAlternateVendorHeaderGeneratedIdentifier() != null && preq.getAlternateVendorDetailAssignedIdentifier() != null && preq.getVendorHeaderGeneratedIdentifier().compareTo(preq.getAlternateVendorHeaderGeneratedIdentifier()) == 0 && preq.getVendorDetailAssignedIdentifier().compareTo(preq.getAlternateVendorDetailAssignedIdentifier()) == 0) {
120                explicitEntry.setTransactionLedgerEntryDescription(entryDescription(preq.getPurchaseOrderDocument().getAlternateVendorName()));
121            }
122
123        } else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) {
124            VendorCreditMemoDocument cm = (VendorCreditMemoDocument) purapDocument;
125            if (cm.isSourceDocumentPaymentRequest()) {
126                // if CM is off of PREQ, use vendor name associated with PREQ (primary or alternate)
127                PaymentRequestDocument cmPR = cm.getPaymentRequestDocument();
128                PurchaseOrderDocument cmPO = cm.getPurchaseOrderDocument();
129                // if alternate payee is paid for non-primary vendor payment, send alternate vendor name in GL desc
130                if (cmPR.getAlternateVendorHeaderGeneratedIdentifier() != null && cmPR.getAlternateVendorDetailAssignedIdentifier() != null && cmPR.getVendorHeaderGeneratedIdentifier().compareTo(cmPR.getAlternateVendorHeaderGeneratedIdentifier()) == 0 && cmPR.getVendorDetailAssignedIdentifier().compareTo(cmPR.getAlternateVendorDetailAssignedIdentifier()) == 0) {
131                    explicitEntry.setTransactionLedgerEntryDescription(entryDescription(cmPO.getAlternateVendorName()));
132                }
133            }
134        } else if (PurapDocTypeCodes.INVOICE_DOCUMENT.equals(docType)) {
135            InvoiceDocument prqs = (InvoiceDocument) purapDocument;
136            if (invoiceService.allowBackpost(prqs)) {
137                LOG.debug("createGlPendingTransaction() within range to allow backpost; posting entry to period 12 of previous FY");
138                explicitEntry.setUniversityFiscalYear(uDate.getUniversityFiscalYear() - 1);
139                explicitEntry.setUniversityFiscalPeriodCode(OLEConstants.MONTH12);
140            }
141
142            // if alternate payee is paid for non-primary vendor payment, send alternate vendor name in GL desc
143            if (prqs.getAlternateVendorHeaderGeneratedIdentifier() != null && prqs.getAlternateVendorDetailAssignedIdentifier() != null &&
144                    prqs.getVendorHeaderGeneratedIdentifier().compareTo(prqs.getAlternateVendorHeaderGeneratedIdentifier()) == 0 && prqs.getVendorDetailAssignedIdentifier().compareTo(prqs.getAlternateVendorDetailAssignedIdentifier()) == 0) {
145                explicitEntry.setTransactionLedgerEntryDescription(entryDescription(prqs.getPurchaseOrderDocument().getAlternateVendorName()));
146            }
147
148        } else {
149            throw new IllegalArgumentException("purapDocument (doc #" + purapDocument.getDocumentNumber() + ") is invalid");
150        }
151
152        ObjectCode objectCode = objectCodeService.getByPrimaryId(explicitEntry.getUniversityFiscalYear(), explicitEntry.getChartOfAccountsCode(), explicitEntry.getFinancialObjectCode());
153        if (ObjectUtils.isNotNull(objectCode)) {
154            explicitEntry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode());
155        }
156
157        SubObjectCode subObjectCode = subObjectCodeService.getByPrimaryId(explicitEntry.getUniversityFiscalYear(), explicitEntry.getChartOfAccountsCode(), explicitEntry.getAccountNumber(), explicitEntry.getFinancialObjectCode(), explicitEntry.getFinancialSubObjectCode());
158        if (ObjectUtils.isNotNull(subObjectCode)) {
159            explicitEntry.setFinancialSubObjectCode(subObjectCode.getFinancialSubObjectCode());
160        }
161
162        if (isEncumbrance) {
163            explicitEntry.setFinancialBalanceTypeCode(BALANCE_TYPE_EXTERNAL_ENCUMBRANCE);
164
165            // D - means the encumbrance is based on the document number
166            // R - means the encumbrance is based on the referring document number
167            // All encumbrances should set the update code to 'R' regardless of if they were created by the PO, PREQ, or CM
168            explicitEntry.setTransactionEncumbranceUpdateCode(ENCUMB_UPDT_REFERENCE_DOCUMENT_CD);
169        }
170
171        // if the amount is negative, flip the D/C indicator
172        if (accountingLine.getAmount().doubleValue() < 0) {
173            if (GL_CREDIT_CODE.equals(debitCreditCode)) {
174                explicitEntry.setTransactionDebitCreditCode(GL_DEBIT_CODE);
175            } else {
176                explicitEntry.setTransactionDebitCreditCode(GL_CREDIT_CODE);
177            }
178        } else {
179            explicitEntry.setTransactionDebitCreditCode(debitCreditCode);
180        }
181
182    }// end purapCustomizeGeneralLedgerPendingEntry()
183
184    /**
185     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesCancelAccountsPayableDocument(org.kuali.ole.module.purap.document.AccountsPayableDocument)
186     */
187    public void generateEntriesCancelAccountsPayableDocument(AccountsPayableDocument apDocument) {
188        LOG.debug("generateEntriesCancelAccountsPayableDocument() started");
189        if (apDocument instanceof PaymentRequestDocument) {
190            LOG.debug("generateEntriesCancelAccountsPayableDocument() cancel PaymentRequestDocument");
191            generateEntriesCancelPaymentRequest((PaymentRequestDocument) apDocument);
192        } else if (apDocument instanceof VendorCreditMemoDocument) {
193            LOG.debug("generateEntriesCancelAccountsPayableDocument() cancel CreditMemoDocument");
194            generateEntriesCancelCreditMemo((VendorCreditMemoDocument) apDocument);
195        } else if (apDocument instanceof InvoiceDocument) {
196            LOG.debug("generateEntriesCancelAccountsPayableDocument() cancel CreditMemoDocument");
197            generateEntriesCancelInvoice((InvoiceDocument) apDocument);
198        } else {
199            // doc not found
200        }
201    }
202
203    /**
204     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesCreatePaymentRequest(org.kuali.ole.module.purap.document.PaymentRequestDocument)
205     */
206    public void generateEntriesCreatePaymentRequest(PaymentRequestDocument preq) {
207        LOG.debug("generateEntriesCreatePaymentRequest() started");
208        List<SourceAccountingLine> encumbrances = relieveEncumbrance(preq);
209        List<SummaryAccount> summaryAccounts = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq);
210        generateEntriesPaymentRequest(preq, encumbrances, summaryAccounts, CREATE_PAYMENT_REQUEST);
211    }
212
213    /**
214     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesCreatePaymentRequest(org.kuali.ole.module.purap.document.PaymentRequestDocument)
215     */
216    public void generateEntriesCreateInvoice(InvoiceDocument inv) {
217        LOG.debug("generateEntriesCreateInvoice() started");
218        List<SourceAccountingLine> encumbrances = relieveEncumbrance(inv);
219        List<SummaryAccount> summaryAccounts = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(inv);
220        generateEntriesInvoice(inv, encumbrances, summaryAccounts, CREATE_INVOICE);
221    }
222
223
224    /**
225     * Called from generateEntriesCancelAccountsPayableDocument() for Payment Request Document
226     *
227     * @param preq Payment Request document to cancel
228     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesCancelAccountsPayableDocument(org.kuali.ole.module.purap.document.AccountsPayableDocument)
229     */
230    protected void generateEntriesCancelPaymentRequest(PaymentRequestDocument preq) {
231        LOG.debug("generateEntriesCreatePaymentRequest() started");
232        List<SourceAccountingLine> encumbrances = reencumberEncumbrance(preq);
233        List<SummaryAccount> summaryAccounts = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq);
234        generateEntriesPaymentRequest(preq, encumbrances, summaryAccounts, CANCEL_PAYMENT_REQUEST);
235    }
236
237
238    protected void generateEntriesCancelInvoice(InvoiceDocument prqs) {
239        LOG.debug("generateEntriesCancelInvoice() started");
240        List<SourceAccountingLine> encumbrances = reencumberEncumbrance(prqs);
241        List<SummaryAccount> summaryAccounts = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(prqs);
242        generateEntriesInvoice(prqs, encumbrances, summaryAccounts, CANCEL_INVOICE);
243    }
244
245
246    /**
247     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesModifyPaymentRequest(org.kuali.ole.module.purap.document.PaymentRequestDocument)
248     */
249    public void generateEntriesModifyPaymentRequest(PaymentRequestDocument preq) {
250        LOG.debug("generateEntriesModifyPaymentRequest() started");
251
252        Map<SourceAccountingLine, KualiDecimal> actualsPositive = new HashMap<SourceAccountingLine, KualiDecimal>();
253        List<SourceAccountingLine> newAccountingLines = purapAccountingService.generateSummaryWithNoZeroTotalsNoUseTax(preq.getItems());
254        for (SourceAccountingLine newAccount : newAccountingLines) {
255            actualsPositive.put(newAccount, newAccount.getAmount());
256            if (LOG.isDebugEnabled()) {
257                LOG.debug("generateEntriesModifyPaymentRequest() actualsPositive: " + newAccount.getAccountNumber() + " = " + newAccount.getAmount());
258            }
259        }
260
261        Map<SourceAccountingLine, KualiDecimal> actualsNegative = new HashMap<SourceAccountingLine, KualiDecimal>();
262        List<AccountsPayableSummaryAccount> oldAccountingLines = purapAccountingService.getAccountsPayableSummaryAccounts(preq.getPurapDocumentIdentifier(), PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT);
263
264        for (AccountsPayableSummaryAccount oldAccount : oldAccountingLines) {
265            actualsNegative.put(oldAccount.generateSourceAccountingLine(), oldAccount.getAmount());
266            if (LOG.isDebugEnabled()) {
267                LOG.debug("generateEntriesModifyPaymentRequest() actualsNegative: " + oldAccount.getAccountNumber() + " = " + oldAccount.getAmount());
268            }
269        }
270
271        // Add the positive entries and subtract the negative entries
272        Map<SourceAccountingLine, KualiDecimal> glEntries = new HashMap<SourceAccountingLine, KualiDecimal>();
273
274        // Combine the two maps (copy all the positive entries)
275        LOG.debug("generateEntriesModifyPaymentRequest() Combine positive/negative entries");
276        glEntries.putAll(actualsPositive);
277
278        for (Iterator<SourceAccountingLine> iter = actualsNegative.keySet().iterator(); iter.hasNext(); ) {
279            SourceAccountingLine key = (SourceAccountingLine) iter.next();
280
281            KualiDecimal amt;
282            if (glEntries.containsKey(key)) {
283                amt = (KualiDecimal) glEntries.get(key);
284                amt = amt.subtract((KualiDecimal) actualsNegative.get(key));
285            } else {
286                amt = ZERO;
287                amt = amt.subtract((KualiDecimal) actualsNegative.get(key));
288            }
289            glEntries.put(key, amt);
290        }
291
292        List<SummaryAccount> summaryAccounts = new ArrayList<SummaryAccount>();
293        for (Iterator<SourceAccountingLine> iter = glEntries.keySet().iterator(); iter.hasNext(); ) {
294            SourceAccountingLine account = (SourceAccountingLine) iter.next();
295            KualiDecimal amount = (KualiDecimal) glEntries.get(account);
296            if (ZERO.compareTo(amount) != 0) {
297                account.setAmount(amount);
298                SummaryAccount sa = new SummaryAccount(account);
299                summaryAccounts.add(sa);
300            }
301        }
302
303        LOG.debug("generateEntriesModifyPaymentRequest() Generate GL entries");
304        generateEntriesPaymentRequest(preq, null, summaryAccounts, MODIFY_PAYMENT_REQUEST);
305    }
306
307
308    /**
309     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesModifyPaymentRequest(org.kuali.ole.module.purap.document.PaymentRequestDocument)
310     */
311    public void generateEntriesModifyInvoice(InvoiceDocument inv) {
312        LOG.debug("generateEntriesModifyPaymentRequest() started");
313        LOG.info("inv.getItems() >>>>>>>>" + inv.getItems());
314        Map<SourceAccountingLine, KualiDecimal> actualsPositive = new HashMap<SourceAccountingLine, KualiDecimal>();
315        List<SourceAccountingLine> newAccountingLines = purapAccountingService.generateSummaryWithNoZeroTotalsNoUseTax(inv.getItems());
316        for (SourceAccountingLine newAccount : newAccountingLines) {
317            actualsPositive.put(newAccount, newAccount.getAmount());
318            if (LOG.isDebugEnabled()) {
319                LOG.debug("generateEntriesModifyPaymentRequest() actualsPositive: " + newAccount.getAccountNumber() + " = " + newAccount.getAmount());
320            }
321        }
322
323        Map<SourceAccountingLine, KualiDecimal> actualsNegative = new HashMap<SourceAccountingLine, KualiDecimal>();
324        List<OleInvoiceAccountsPayableSummaryAccount> oldAccountingLines = purapAccountingService.getAccountsPayableSummaryAccounts(inv.getPurapDocumentIdentifier(), PurapDocTypeCodes.INVOICE_DOCUMENT);
325
326        for (OleInvoiceAccountsPayableSummaryAccount oldAccount : oldAccountingLines) {
327            actualsNegative.put(oldAccount.generateSourceAccountingLine(), oldAccount.getAmount());
328            if (LOG.isDebugEnabled()) {
329                LOG.debug("generateEntriesModifyPaymentRequest() actualsNegative: " + oldAccount.getAccountNumber() + " = " + oldAccount.getAmount());
330            }
331        }
332
333        // Add the positive entries and subtract the negative entries
334        Map<SourceAccountingLine, KualiDecimal> glEntries = new HashMap<SourceAccountingLine, KualiDecimal>();
335
336        // Combine the two maps (copy all the positive entries)
337        LOG.debug("generateEntriesModifyPaymentRequest() Combine positive/negative entries");
338        glEntries.putAll(actualsPositive);
339
340        for (Iterator<SourceAccountingLine> iter = actualsNegative.keySet().iterator(); iter.hasNext(); ) {
341            SourceAccountingLine key = (SourceAccountingLine) iter.next();
342
343            KualiDecimal amt;
344            if (glEntries.containsKey(key)) {
345                amt = (KualiDecimal) glEntries.get(key);
346                amt = amt.subtract((KualiDecimal) actualsNegative.get(key));
347            } else {
348                amt = ZERO;
349                amt = amt.subtract((KualiDecimal) actualsNegative.get(key));
350            }
351            glEntries.put(key, amt);
352        }
353
354        List<SummaryAccount> summaryAccounts = new ArrayList<SummaryAccount>();
355        for (Iterator<SourceAccountingLine> iter = glEntries.keySet().iterator(); iter.hasNext(); ) {
356            SourceAccountingLine account = (SourceAccountingLine) iter.next();
357            KualiDecimal amount = (KualiDecimal) glEntries.get(account);
358            if (ZERO.compareTo(amount) != 0) {
359                account.setAmount(amount);
360                SummaryAccount sa = new SummaryAccount(account);
361                summaryAccounts.add(sa);
362            }
363        }
364
365        LOG.debug("generateEntriesModifyInvoice() Generate GL entries");
366        generateEntriesInvoice(inv, null, summaryAccounts, MODIFY_INVOICE);
367    }
368
369    /**
370     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesCreateCreditMemo(VendorCreditMemoDocument)
371     */
372    public void generateEntriesCreateCreditMemo(VendorCreditMemoDocument cm) {
373        LOG.debug("generateEntriesCreateCreditMemo() started");
374        generateEntriesCreditMemo(cm, CREATE_CREDIT_MEMO);
375    }
376
377    /**
378     * Called from generateEntriesCancelAccountsPayableDocument() for Payment Request Document
379     *
380     * @param cm VendorCreditMemoDocument to cancel
381     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesCancelAccountsPayableDocument(org.kuali.ole.module.purap.document.AccountsPayableDocument)
382     */
383    protected void generateEntriesCancelCreditMemo(VendorCreditMemoDocument cm) {
384        LOG.debug("generateEntriesCancelCreditMemo() started");
385        if(!(cm != null && cm.getDocumentHeader() != null && cm.getDocumentHeader().getWorkflowDocument() != null && cm.getDocumentHeader().getWorkflowDocument().isDisapproved())) {
386            generateEntriesCreditMemo(cm, CANCEL_CREDIT_MEMO);
387        }
388    }
389
390    /**
391     * Retrieves the next available sequence number from the general ledger pending entry table for this document
392     *
393     * @param documentNumber Document number to find next sequence number
394     * @return Next available sequence number
395     */
396    protected int getNextAvailableSequence(String documentNumber) {
397        LOG.debug("getNextAvailableSequence() started");
398        Map fieldValues = new HashMap();
399        fieldValues.put("financialSystemOriginationCode", PURAP_ORIGIN_CODE);
400        fieldValues.put("documentNumber", documentNumber);
401        int count = businessObjectService.countMatching(GeneralLedgerPendingEntry.class, fieldValues);
402        return count + 1;
403    }
404
405    /**
406     * Creates the general ledger entries for Payment Request actions.
407     *
408     * @param preq            Payment Request document to create entries
409     * @param encumbrances    List of encumbrance accounts if applies
410     * @param summaryAccounts List of preq accounts to create entries
411     * @param processType     Type of process (create, modify, cancel)
412     * @return Boolean returned indicating whether entry creation succeeded
413     */
414    protected boolean generateEntriesPaymentRequest(PaymentRequestDocument preq, List encumbrances, List summaryAccounts, String processType) {
415        LOG.debug("generateEntriesPaymentRequest() started");
416        boolean success = true;
417        preq.setGeneralLedgerPendingEntries(new ArrayList());
418
419        /*
420         * Can't let generalLedgerPendingEntryService just create all the entries because we need the sequenceHelper to carry over
421         * from the encumbrances to the actuals and also because we need to tell the PaymentRequestDocumentRule customize entry
422         * method how to customize differently based on if creating an encumbrance or actual.
423         */
424        GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper(getNextAvailableSequence(preq.getDocumentNumber()));
425
426        // when cancelling a PREQ, do not book encumbrances if PO is CLOSED
427        if (encumbrances != null && !(CANCEL_PAYMENT_REQUEST.equals(processType) && PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED.equals(preq.getPurchaseOrderDocument().getApplicationDocumentStatus()))  && preq.getDocumentHeader() != null && preq.getDocumentHeader().getWorkflowDocument()!= null && !preq.getDocumentHeader().getWorkflowDocument().isDisapproved()) {
428            LOG.debug("generateEntriesPaymentRequest() generate encumbrance entries");
429            if (CREATE_PAYMENT_REQUEST.equals(processType)) {
430                // on create, use CREDIT code for encumbrances
431                preq.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
432            } else if (CANCEL_PAYMENT_REQUEST.equals(processType)) {
433                // on cancel, use DEBIT code
434                preq.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
435            } else if (MODIFY_PAYMENT_REQUEST.equals(processType)) {
436                // no encumbrances for modify
437            }
438
439            preq.setGenerateEncumbranceEntries(true);
440            for (Iterator iter = encumbrances.iterator(); iter.hasNext(); ) {
441                AccountingLine accountingLine = (AccountingLine) iter.next();
442                preq.generateGeneralLedgerPendingEntries(accountingLine, sequenceHelper);
443                sequenceHelper.increment(); // increment for the next line
444            }
445        }
446
447        if (ObjectUtils.isNotNull(summaryAccounts) && !summaryAccounts.isEmpty()) {
448            LOG.debug("generateEntriesPaymentRequest() now book the actuals");
449            preq.setGenerateEncumbranceEntries(false);
450
451            if (CREATE_PAYMENT_REQUEST.equals(processType) || MODIFY_PAYMENT_REQUEST.equals(processType)) {
452                // on create and modify, use DEBIT code
453                preq.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
454            } else if (CANCEL_PAYMENT_REQUEST.equals(processType)) {
455                // on cancel, use CREDIT code
456                preq.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
457            }
458
459            for (Iterator iter = summaryAccounts.iterator(); iter.hasNext(); ) {
460                SummaryAccount summaryAccount = (SummaryAccount) iter.next();
461                Account itemAccount;
462                KualiDecimal prorateSurcharge = KualiDecimal.ZERO;
463                for (Iterator<PaymentRequestItem> preqIter = preq.getItems().iterator(); preqIter.hasNext(); ) {
464                    OlePaymentRequestItem item = (OlePaymentRequestItem) preqIter.next();
465                    List<PurApAccountingLine> itemAccLineList = item.getSourceAccountingLines();
466                    if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice() != null && item.getExtendedPrice().compareTo(KualiDecimal.ZERO) != 0) {
467                        if (item.getItemSurcharge() != null && item.getItemTypeCode().equals("ITEM")) {
468                            prorateSurcharge = new KualiDecimal(item.getItemSurcharge()).multiply(item.getItemQuantity());
469                        }
470
471                    }
472                    for (PurApAccountingLine itemAccLine : itemAccLineList) {
473                        itemAccount = itemAccLine.getAccount();
474                        if ((itemAccount.getAccountNumber().equals(summaryAccount.getAccount().getAccountNumber())) && ((itemAccount.getChartOfAccountsCode().equals(summaryAccount.getAccount().getChartOfAccountsCode())))) {
475                            summaryAccount.getAccount().setAmount(summaryAccount.getAccount().getAmount().subtract(prorateSurcharge));
476                        }
477                        prorateSurcharge = KualiDecimal.ZERO;
478                    }
479                }
480                KualiDecimal summaryAmount = summaryAccount.getAccount().getAmount();
481                if (summaryAmount.isNonZero() && preq.getDocumentHeader() != null && preq.getDocumentHeader().getWorkflowDocument()!= null && !preq.getDocumentHeader().getWorkflowDocument().isDisapproved()) {
482                    preq.generateGeneralLedgerPendingEntries(summaryAccount.getAccount(), sequenceHelper);
483                    sequenceHelper.increment(); // increment for the next line
484                }
485            }
486
487            // generate offset accounts for use tax if it exists (useTaxContainers will be empty if not a use tax document)
488            List<UseTaxContainer> useTaxContainers = purapAccountingService.generateUseTaxAccount(preq);
489            for (UseTaxContainer useTaxContainer : useTaxContainers) {
490                PurApItemUseTax offset = useTaxContainer.getUseTax();
491                List<SourceAccountingLine> accounts = useTaxContainer.getAccounts();
492                for (SourceAccountingLine sourceAccountingLine : accounts) {
493                    preq.generateGeneralLedgerPendingEntries(sourceAccountingLine, sequenceHelper, useTaxContainer.getUseTax());
494                    sequenceHelper.increment(); // increment for the next line
495                }
496
497            }
498
499            // Manually save preq summary accounts
500            if (MODIFY_PAYMENT_REQUEST.equals(processType)) {
501                //for modify, regenerate the summary from the doc
502                List<SummaryAccount> summaryAccountsForModify = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq);
503                saveAccountsPayableSummaryAccounts(summaryAccountsForModify, preq.getPurapDocumentIdentifier(), PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT);
504            } else {
505                //for create and cancel, use the summary accounts
506                saveAccountsPayableSummaryAccounts(summaryAccounts, preq.getPurapDocumentIdentifier(), PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT);
507            }
508
509            // manually save cm account change tables (CAMS needs this)
510            if (CREATE_PAYMENT_REQUEST.equals(processType) || MODIFY_PAYMENT_REQUEST.equals(processType)) {
511                SpringContext.getBean(PurapAccountRevisionService.class).savePaymentRequestAccountRevisions(preq.getItems(), preq.getPostingYearFromPendingGLEntries(), preq.getPostingPeriodCodeFromPendingGLEntries());
512            } else if (CANCEL_PAYMENT_REQUEST.equals(processType)) {
513                SpringContext.getBean(PurapAccountRevisionService.class).cancelPaymentRequestAccountRevisions(preq.getItems(), preq.getPostingYearFromPendingGLEntries(), preq.getPostingPeriodCodeFromPendingGLEntries());
514            }
515        }
516
517
518        // Manually save GL entries for Payment Request and encumbrances
519        saveGLEntries(preq.getGeneralLedgerPendingEntries());
520
521        return success;
522    }
523
524    /**
525     * Creates the general ledger entries for Invoice actions.
526     *
527     * @param inv             Invoice document to create entries
528     * @param encumbrances    List of encumbrance accounts if applies
529     * @param summaryAccounts List of Invoice accounts to create entries
530     * @param processType     Type of process (create, modify, cancel)
531     * @return Boolean returned indicating whether entry creation succeeded
532     */
533    protected boolean generateEntriesInvoice(InvoiceDocument inv, List encumbrances, List summaryAccounts, String processType) {
534        LOG.debug("generateEntriesPaymentRequest() started");
535        boolean success = true;
536        inv.setGeneralLedgerPendingEntries(new ArrayList());
537
538        /**
539         * Can't let generalLedgerPendingEntryService just create all the entries because we need the sequenceHelper to carry over
540         * from the encumbrances to the actuals and also because we need to tell the PaymentRequestDocumentRule customize entry
541         * method how to customize differently based on if creating an encumbrance or actual.
542         */
543        GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper(getNextAvailableSequence(inv.getDocumentNumber()));
544
545        // when cancelling a PREQ, do not book encumbrances if PO is CLOSED
546        if (encumbrances != null && !(CANCEL_INVOICE.equals(processType) )) {
547            LOG.debug("generateEntriesInvoice() generate encumbrance entries");
548            if (CREATE_INVOICE.equals(processType)) {
549                // on create, use CREDIT code for encumbrances
550                inv.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
551            } else if (CANCEL_INVOICE.equals(processType)) {
552                // on cancel, use DEBIT code
553                inv.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
554            } else if (MODIFY_INVOICE.equals(processType)) {
555                // no encumbrances for modify
556            }
557
558            inv.setGenerateEncumbranceEntries(true);
559            for (Iterator iter = encumbrances.iterator(); iter.hasNext(); ) {
560                AccountingLine accountingLine = (AccountingLine) iter.next();
561                inv.generateGeneralLedgerPendingEntries(accountingLine, sequenceHelper);
562                sequenceHelper.increment(); // increment for the next line
563            }
564        }
565
566        if (ObjectUtils.isNotNull(summaryAccounts) && !summaryAccounts.isEmpty()) {
567            LOG.debug("generateEntriesInvoice() now book the actuals");
568            inv.setGenerateEncumbranceEntries(false);
569
570            if (CREATE_INVOICE.equals(processType) || MODIFY_INVOICE.equals(processType)) {
571                // on create and modify, use DEBIT code
572                inv.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
573            } else if (CANCEL_INVOICE.equals(processType)) {
574                // on cancel, use CREDIT code
575                inv.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
576            }
577
578            for (Iterator iter = summaryAccounts.iterator(); iter.hasNext(); ) {
579                SummaryAccount summaryAccount = (SummaryAccount) iter.next();
580                Account itemAccount;
581                KualiDecimal prorateSurcharge = KualiDecimal.ZERO;
582                for (Iterator<OleInvoiceItem> preqIter = inv.getItems().iterator(); preqIter.hasNext(); ) {
583                    OleInvoiceItem item = (OleInvoiceItem) preqIter.next();
584                    List<PurApAccountingLine> itemAccLineList = item.getSourceAccountingLines();
585                    if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice() != null && item.getExtendedPrice().compareTo(KualiDecimal.ZERO) != 0) {
586                        if (item.getItemSurcharge() != null && item.getItemTypeCode().equals("ITEM")) {
587                            prorateSurcharge = new KualiDecimal(item.getItemSurcharge()).multiply(item.getItemQuantity());
588                        }
589
590                    }
591                    for (PurApAccountingLine itemAccLine : itemAccLineList) {
592                        itemAccount = itemAccLine.getAccount();
593                        if ((itemAccount.getAccountNumber().equals(summaryAccount.getAccount().getAccountNumber())) && ((itemAccount.getChartOfAccountsCode().equals(summaryAccount.getAccount().getChartOfAccountsCode())))) {
594                            summaryAccount.getAccount().setAmount(summaryAccount.getAccount().getAmount().subtract(prorateSurcharge));
595                        }
596                        prorateSurcharge = KualiDecimal.ZERO;
597                    }
598                }
599                KualiDecimal summaryAmount = summaryAccount.getAccount().getAmount();
600                if (summaryAmount.isNonZero()) {
601                    inv.generateGeneralLedgerPendingEntries(summaryAccount.getAccount(), sequenceHelper);
602                    sequenceHelper.increment(); // increment for the next line
603                }
604            }
605
606            // generate offset accounts for use tax if it exists (useTaxContainers will be empty if not a use tax document)
607            List<UseTaxContainer> useTaxContainers = purapAccountingService.generateUseTaxAccount(inv);
608            for (UseTaxContainer useTaxContainer : useTaxContainers) {
609                PurApItemUseTax offset = useTaxContainer.getUseTax();
610                List<SourceAccountingLine> accounts = useTaxContainer.getAccounts();
611                for (SourceAccountingLine sourceAccountingLine : accounts) {
612                    inv.generateGeneralLedgerPendingEntries(sourceAccountingLine, sequenceHelper, useTaxContainer.getUseTax());
613                    sequenceHelper.increment(); // increment for the next line
614                }
615
616            }
617
618            // Manually save Invoice summary accounts
619            if (MODIFY_INVOICE.equals(processType)) {
620                //for modify, regenerate the summary from the doc
621                List<SummaryAccount> summaryAccountsForModify = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(inv);
622                saveInvoiceAccountsPayableSummaryAccounts(summaryAccountsForModify, inv.getPurapDocumentIdentifier(), PurapDocTypeCodes.INVOICE_DOCUMENT);
623            } else {
624                //for create and cancel, use the summary accounts
625                saveInvoiceAccountsPayableSummaryAccounts(summaryAccounts, inv.getPurapDocumentIdentifier(), PurapDocTypeCodes.INVOICE_DOCUMENT);
626            }
627
628            // manually save cm account change tables (CAMS needs this)
629            if (CREATE_INVOICE.equals(processType) || MODIFY_INVOICE.equals(processType)) {
630                SpringContext.getBean(PurapAccountRevisionService.class).saveInvoiceAccountRevisions(inv.getItems(), inv.getPostingYearFromPendingGLEntries(), inv.getPostingPeriodCodeFromPendingGLEntries());
631            } else if (CANCEL_INVOICE.equals(processType)) {
632                SpringContext.getBean(PurapAccountRevisionService.class).cancelInvoiceAccountRevisions(inv.getItems(), inv.getPostingYearFromPendingGLEntries(), inv.getPostingPeriodCodeFromPendingGLEntries());
633            }
634        }
635
636
637        // Manually save GL entries for Invoice and encumbrances
638        //  saveGLEntries(inv.getGeneralLedgerPendingEntries());
639
640        return success;
641    }
642
643    /**
644     * Creates the general ledger entries for Credit Memo actions.
645     *
646     * @param cm       Credit Memo document to create entries
647     * @param isCancel Indicates if request is a cancel or create
648     * @return Boolean returned indicating whether entry creation succeeded
649     */
650    protected boolean generateEntriesCreditMemo(VendorCreditMemoDocument cm, boolean isCancel) {
651        LOG.debug("generateEntriesCreditMemo() started");
652
653        cm.setGeneralLedgerPendingEntries(new ArrayList());
654
655        boolean success = true;
656        GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper(getNextAvailableSequence(cm.getDocumentNumber()));
657
658        if (!cm.isSourceVendor()) {
659            LOG.debug("generateEntriesCreditMemo() create encumbrance entries for CM against a PO or PREQ (not vendor)");
660            PurchaseOrderDocument po = null;
661            if (cm.isSourceDocumentPurchaseOrder()) {
662                LOG.debug("generateEntriesCreditMemo() PO type");
663                po = purchaseOrderService.getCurrentPurchaseOrder(cm.getPurchaseOrderIdentifier());
664            } else if (cm.isSourceDocumentPaymentRequest()) {
665                LOG.debug("generateEntriesCreditMemo() PREQ type");
666                po = purchaseOrderService.getCurrentPurchaseOrder(cm.getPaymentRequestDocument().getPurchaseOrderIdentifier());
667            }
668
669            // for CM cancel or create, do not book encumbrances if PO is CLOSED, but do update the amounts on the PO
670            //  Coomented for JIRA:OLE-5162 as encumbrance added here
671            //  UnCommented for JIRA:OLE-6992 as encumbrance added here for positive values
672            List encumbrances = getCreditMemoEncumbrance(cm, po, isCancel);
673            if (!(PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED.equals(po.getApplicationDocumentStatus()))) {
674                if (encumbrances != null) {
675                    cm.setGenerateEncumbranceEntries(true);
676
677                    // even if generating encumbrance entries on cancel, call is the same because the method gets negative amounts
678                    // from
679                    // the map so Debits on negatives = a credit
680                    cm.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
681
682                    for (Iterator iter = encumbrances.iterator(); iter.hasNext(); ) {
683                        AccountingLine accountingLine = (AccountingLine) iter.next();
684                        if (accountingLine.getAmount().compareTo(ZERO) != 0) {
685                            cm.generateGeneralLedgerPendingEntries(accountingLine, sequenceHelper);
686                            sequenceHelper.increment(); // increment for the next line
687                        }
688                    }
689                }
690            }
691        }
692
693        List<SummaryAccount> summaryAccounts = purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(cm);
694        if (summaryAccounts != null) {
695            LOG.debug("generateEntriesCreditMemo() now book the actuals");
696            cm.setGenerateEncumbranceEntries(false);
697
698            if (!isCancel) {
699                // on create, use CREDIT code
700                cm.setDebitCreditCodeForGLEntries(GL_CREDIT_CODE);
701            } else {
702                // on cancel, use DEBIT code
703                cm.setDebitCreditCodeForGLEntries(GL_DEBIT_CODE);
704            }
705
706            for (Iterator iter = summaryAccounts.iterator(); iter.hasNext(); ) {
707                SummaryAccount summaryAccount = (SummaryAccount) iter.next();
708                cm.generateGeneralLedgerPendingEntries(summaryAccount.getAccount(), sequenceHelper);
709                sequenceHelper.increment(); // increment for the next line
710            }
711            // generate offset accounts for use tax if it exists (useTaxContainers will be empty if not a use tax document)
712            List<UseTaxContainer> useTaxContainers = purapAccountingService.generateUseTaxAccount(cm);
713            for (UseTaxContainer useTaxContainer : useTaxContainers) {
714                PurApItemUseTax offset = useTaxContainer.getUseTax();
715                List<SourceAccountingLine> accounts = useTaxContainer.getAccounts();
716                for (SourceAccountingLine sourceAccountingLine : accounts) {
717                    cm.generateGeneralLedgerPendingEntries(sourceAccountingLine, sequenceHelper, useTaxContainer.getUseTax());
718                    sequenceHelper.increment(); // increment for the next line
719                }
720
721            }
722
723            // manually save cm account change tables (CAMS needs this)
724            if (!isCancel) {
725                SpringContext.getBean(PurapAccountRevisionService.class).saveCreditMemoAccountRevisions(cm.getItems(), cm.getPostingYearFromPendingGLEntries(), cm.getPostingPeriodCodeFromPendingGLEntries());
726            } else {
727                SpringContext.getBean(PurapAccountRevisionService.class).cancelCreditMemoAccountRevisions(cm.getItems(), cm.getPostingYearFromPendingGLEntries(), cm.getPostingPeriodCodeFromPendingGLEntries());
728            }
729        }
730
731        saveGLEntries(cm.getGeneralLedgerPendingEntries());
732
733        LOG.debug("generateEntriesCreditMemo() ended");
734        return success;
735    }
736
737    /**
738     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesApproveAmendPurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
739     */
740    public void generateEntriesApproveAmendPurchaseOrder(PurchaseOrderDocument po) {
741        LOG.debug("generateEntriesApproveAmendPurchaseOrder() started");
742
743        // Set outstanding encumbered quantity/amount on items
744        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
745            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
746
747            // if invoice fields are null (as would be for new items), set fields to zero
748            item.setItemInvoicedTotalAmount(item.getItemInvoicedTotalAmount() == null ? ZERO : item.getItemInvoicedTotalAmount());
749            item.setItemInvoicedTotalQuantity(item.getItemInvoicedTotalQuantity() == null ? ZERO : item.getItemInvoicedTotalQuantity());
750
751            if (!item.isItemActiveIndicator()) {
752                // set outstanding encumbrance amounts to zero for inactive items
753                item.setItemOutstandingEncumberedQuantity(ZERO);
754                item.setItemOutstandingEncumberedAmount(ZERO);
755
756                for (Iterator iter = item.getSourceAccountingLines().iterator(); iter.hasNext(); ) {
757                    PurchaseOrderAccount account = (PurchaseOrderAccount) iter.next();
758                    account.setItemAccountOutstandingEncumbranceAmount(ZERO);
759                    account.setAlternateAmountForGLEntryCreation(ZERO);
760                }
761            } else {
762                // Set quantities
763                //Because of Quantity is defaulted to 1, the Additional charges also having the same quantity. So checking for the Unit Price also.
764                if (item.getItemQuantity() != null && item.getItemUnitPrice() != null) {
765                    item.setItemOutstandingEncumberedQuantity(item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity()));
766                } else {
767                    // if order qty is null, outstanding encumbered qty should be null
768                    item.setItemOutstandingEncumberedQuantity(null);
769                }
770
771                // Set amount
772                if (item.getItemOutstandingEncumberedQuantity() != null && item.getItemOutstandingEncumberedQuantity().isGreaterThan(new KualiDecimal(0))) {
773                    //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
774                    KualiDecimal itemEncumber = new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue().multiply(item.getItemUnitPrice()));
775
776                    //add tax for encumbrance
777                    KualiDecimal itemTaxAmount = item.getItemTaxAmount() == null ? ZERO : item.getItemTaxAmount();
778                    itemEncumber = itemEncumber.add(itemTaxAmount);
779
780                    item.setItemOutstandingEncumberedAmount(itemEncumber);
781                }
782                else if(item.getItemOutstandingEncumberedQuantity() != null && item.getItemOutstandingEncumberedQuantity().isLessEqual(new KualiDecimal(0))) {
783                    KualiDecimal itemEncumber = new KualiDecimal(item.getItemQuantity().bigDecimalValue().multiply(item.getItemUnitPrice()));
784
785                    //add tax for encumbrance
786                    KualiDecimal itemTaxAmount = item.getItemTaxAmount() == null ? ZERO : item.getItemTaxAmount();
787                    itemEncumber = itemEncumber.add(itemTaxAmount);
788
789                    item.setItemOutstandingEncumberedAmount(itemEncumber);
790
791                }
792                else
793                {
794                    if (item.getItemUnitPrice() != null) {
795                        item.setItemOutstandingEncumberedAmount(new KualiDecimal(item.getItemUnitPrice().subtract(item.getItemInvoicedTotalAmount().bigDecimalValue())));
796                    }
797                }
798
799                for (Iterator iter = item.getSourceAccountingLines().iterator(); iter.hasNext(); ) {
800                    PurchaseOrderAccount account = (PurchaseOrderAccount) iter.next();
801                    BigDecimal percent = new BigDecimal(account.getAccountLinePercent().toString());
802                    percent = percent.divide(new BigDecimal("100"), 3, BigDecimal.ROUND_HALF_UP);
803                    account.setItemAccountOutstandingEncumbranceAmount(item.getItemOutstandingEncumberedAmount().multiply(new KualiDecimal(percent)));
804                    account.setAlternateAmountForGLEntryCreation(account.getItemAccountOutstandingEncumbranceAmount());
805                }
806            }
807        }
808
809        PurchaseOrderDocument oldPO = purchaseOrderService.getCurrentPurchaseOrder(po.getPurapDocumentIdentifier());
810
811        if (oldPO == null) {
812            throw new IllegalArgumentException("Current Purchase Order not found - poId = " + oldPO.getPurapDocumentIdentifier());
813        }
814
815        List newAccounts = purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly());
816        List oldAccounts = purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(oldPO.getItemsActiveOnlySetupAlternateAmount());
817
818        Map combination = new HashMap();
819
820        // Add amounts from the new PO
821        for (Iterator iter = newAccounts.iterator(); iter.hasNext(); ) {
822            SourceAccountingLine newAccount = (SourceAccountingLine) iter.next();
823            combination.put(newAccount, newAccount.getAmount());
824        }
825
826        LOG.debug("generateEntriesApproveAmendPurchaseOrder() combination after the add");
827        for (Iterator iter = combination.keySet().iterator(); iter.hasNext(); ) {
828            SourceAccountingLine element = (SourceAccountingLine) iter.next();
829            if (LOG.isDebugEnabled())
830                LOG.debug("generateEntriesApproveAmendPurchaseOrder() " + element + " = " + ((KualiDecimal) combination.get(element)).floatValue());
831        }
832
833        // Subtract the amounts from the old PO
834        for (Iterator iter = oldAccounts.iterator(); iter.hasNext(); ) {
835            SourceAccountingLine oldAccount = (SourceAccountingLine) iter.next();
836            if (combination.containsKey(oldAccount)) {
837                KualiDecimal amount = (KualiDecimal) combination.get(oldAccount);
838                amount = amount.subtract(oldAccount.getAmount());
839                combination.put(oldAccount, amount);
840            } else {
841                combination.put(oldAccount, ZERO.subtract(oldAccount.getAmount()));
842            }
843        }
844
845        LOG.debug("generateEntriesApproveAmendPurchaseOrder() combination after the subtract");
846        for (Iterator iter = combination.keySet().iterator(); iter.hasNext(); ) {
847            SourceAccountingLine element = (SourceAccountingLine) iter.next();
848            if (LOG.isDebugEnabled())
849                LOG.debug("generateEntriesApproveAmendPurchaseOrder() " + element + " = " + ((KualiDecimal) combination.get(element)).floatValue());
850        }
851
852        List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
853        for (Iterator iter = combination.keySet().iterator(); iter.hasNext(); ) {
854            SourceAccountingLine account = (SourceAccountingLine) iter.next();
855            KualiDecimal amount = (KualiDecimal) combination.get(account);
856            if (ZERO.compareTo(amount) != 0) {
857                account.setAmount(amount);
858                encumbranceAccounts.add(account);
859            }
860        }
861
862        po.setGlOnlySourceAccountingLines(encumbranceAccounts);
863        generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
864        saveGLEntries(po.getGeneralLedgerPendingEntries());
865        LOG.debug("generateEntriesApproveAmendPo() gl entries created; exit method");
866    }
867
868    /**
869     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesClosePurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
870     */
871    public void generateEntriesClosePurchaseOrder(PurchaseOrderDocument po) {
872        LOG.debug("generateEntriesClosePurchaseOrder() started");
873
874        // Set outstanding encumbered quantity/amount on items
875        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
876            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
877
878            String logItmNbr = "Item # " + item.getItemLineNumber();
879
880            if (!item.isItemActiveIndicator()) {
881                continue;
882            }
883
884            KualiDecimal itemAmount = null;
885            if (LOG.isDebugEnabled()) {
886                LOG.debug("generateEntriesClosePurchaseOrder() " + logItmNbr + " Calculate based on amounts");
887            }
888            itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
889
890            KualiDecimal accountTotal = ZERO;
891            PurchaseOrderAccount lastAccount = null;
892            if (itemAmount.compareTo(ZERO) != 0) {
893                // Sort accounts
894                Collections.sort((List) item.getSourceAccountingLines());
895
896                for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext(); ) {
897                    PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
898                    if (!acct.isEmpty()) {
899                        KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
900                        accountTotal = accountTotal.add(acctAmount);
901                        acct.setAlternateAmountForGLEntryCreation(acctAmount);
902                        lastAccount = acct;
903                    }
904                }
905
906                // account for rounding by adjusting last account as needed
907                if (lastAccount != null) {
908                    KualiDecimal difference = itemAmount.subtract(accountTotal);
909                    if (LOG.isDebugEnabled()) {
910                        LOG.debug("generateEntriesClosePurchaseOrder() difference: " + logItmNbr + " " + difference);
911                    }
912
913                    KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
914                    if (ObjectUtils.isNotNull(amount)) {
915                        lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
916                    } else {
917                        lastAccount.setAlternateAmountForGLEntryCreation(difference);
918                    }
919                }
920
921            }
922        }// endfor
923
924        po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
925        if (shouldGenerateGLPEForPurchaseOrder(po)) {
926            generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
927            saveGLEntries(po.getGeneralLedgerPendingEntries());
928            LOG.debug("generateEntriesClosePurchaseOrder() gl entries created; exit method");
929        }
930
931        //MSU Contribution DTT-3812 OLEMI-8642 OLECNTRB-957
932        // Set outstanding encumbered quantity/amount on items
933        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
934            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
935            if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
936                item.setItemOutstandingEncumberedQuantity(KualiDecimal.ZERO);
937            }
938            item.setItemOutstandingEncumberedAmount(KualiDecimal.ZERO);
939            List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
940            for (PurApAccountingLine purApAccountingLine : sourceAccountingLines) {
941                PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine;
942                account.setItemAccountOutstandingEncumbranceAmount(KualiDecimal.ZERO);
943            }
944
945        }// endfor
946
947        LOG.debug("generateEntriesClosePurchaseOrder() no gl entries created because the amount is 0; exit method");
948    }
949
950    /**
951     * We should not generate general ledger pending entries for Purchase Order Close Document and
952     * Purchase Order Reopen Document with $0 amount.
953     *
954     * @param po
955     * @return
956     */
957    protected boolean shouldGenerateGLPEForPurchaseOrder(PurchaseOrderDocument po) {
958        for (SourceAccountingLine acct : (List<SourceAccountingLine>) po.getSourceAccountingLines()) {
959            if (acct.getAmount().abs().compareTo(new KualiDecimal(0)) > 0) {
960                return true;
961            }
962        }
963        return false;
964    }
965
966    /**
967     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesReopenPurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
968     */
969    public void generateEntriesReopenPurchaseOrder(PurchaseOrderDocument po) {
970        LOG.debug("generateEntriesReopenPurchaseOrder() started");
971
972        //MSU Contribution DTT-3812 OLEMI-8642 OLECNTRB-957
973        // Set outstanding encumbered quantity/amount on items
974        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
975            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
976
977            // if invoice fields are null (as would be for new items), set fields to zero
978            item.setItemInvoicedTotalAmount(item.getItemInvoicedTotalAmount() == null ? ZERO : item.getItemInvoicedTotalAmount());
979            item.setItemInvoicedTotalQuantity(item.getItemInvoicedTotalQuantity() == null ? ZERO : item.getItemInvoicedTotalQuantity());
980
981            if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
982                item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity());
983                item.setItemOutstandingEncumberedQuantity(item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity()));
984                if(item.getItemOutstandingEncumberedQuantity().isGreaterThan(new KualiDecimal(0))) {
985                    item.setItemOutstandingEncumberedAmount(new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue().multiply(item.getItemUnitPrice())));
986                }
987                else {
988                    item.setItemOutstandingEncumberedAmount(new KualiDecimal(item.getItemQuantity().bigDecimalValue().multiply(item.getItemUnitPrice())));
989                    item.setItemOutstandingEncumberedQuantity(new KualiDecimal(item.getItemQuantity().bigDecimalValue()));
990                }
991            } else {
992                if((item.getTotalAmount().subtract(item.getItemInvoicedTotalAmount()).isGreaterThan(new KualiDecimal(0)))) {
993                    item.setItemOutstandingEncumberedAmount(item.getTotalAmount().subtract(item.getItemInvoicedTotalAmount()));
994                }
995                else {
996                    item.setItemOutstandingEncumberedAmount(item.getTotalAmount());
997                    item.setItemOutstandingEncumberedQuantity(new KualiDecimal(item.getItemQuantity().bigDecimalValue()));
998                }
999            }
1000            List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
1001            for (PurApAccountingLine purApAccountingLine : sourceAccountingLines) {
1002                PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine;
1003                account.setItemAccountOutstandingEncumbranceAmount(new KualiDecimal(item.getItemOutstandingEncumberedAmount().bigDecimalValue().multiply(account.getAccountLinePercent()).divide(OLEConstants.ONE_HUNDRED.bigDecimalValue())));
1004            }
1005        }// endfor
1006
1007        // Set outstanding encumbered quantity/amount on items
1008        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
1009            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
1010
1011            String logItmNbr = "Item # " + item.getItemLineNumber();
1012
1013            if (!item.isItemActiveIndicator()) {
1014                continue;
1015            }
1016
1017            KualiDecimal itemAmount = null;
1018            if (item.getItemType().isAmountBasedGeneralLedgerIndicator()) {
1019                if (LOG.isDebugEnabled()) {
1020                    LOG.debug("generateEntriesReopenPurchaseOrder() " + logItmNbr + " Calculate based on amounts");
1021                }
1022                itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
1023            } else {
1024                if (LOG.isDebugEnabled()) {
1025                    LOG.debug("generateEntriesReopenPurchaseOrder() " + logItmNbr + " Calculate based on quantities");
1026                }
1027                //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
1028                itemAmount = new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue().multiply(item.getItemUnitPrice()));
1029            }
1030
1031            KualiDecimal accountTotal = ZERO;
1032            PurchaseOrderAccount lastAccount = null;
1033            if (itemAmount.compareTo(ZERO) != 0) {
1034                // Sort accounts
1035                Collections.sort((List) item.getSourceAccountingLines());
1036
1037                for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext(); ) {
1038                    PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
1039                    if (!acct.isEmpty()) {
1040                        KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
1041                        accountTotal = accountTotal.add(acctAmount);
1042                        acct.setAlternateAmountForGLEntryCreation(acctAmount);
1043                        lastAccount = acct;
1044                    }
1045                }
1046
1047                // account for rounding by adjusting last account as needed
1048                if (lastAccount != null) {
1049                    KualiDecimal difference = itemAmount.subtract(accountTotal);
1050                    if (LOG.isDebugEnabled()) {
1051                        LOG.debug("generateEntriesReopenPurchaseOrder() difference: " + logItmNbr + " " + difference);
1052                    }
1053
1054                    KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
1055                    if (ObjectUtils.isNotNull(amount)) {
1056                        lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
1057                    } else {
1058                        lastAccount.setAlternateAmountForGLEntryCreation(difference);
1059                    }
1060                }
1061
1062            }
1063        }// endfor
1064
1065        po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
1066        if (shouldGenerateGLPEForPurchaseOrder(po)) {
1067            generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
1068            saveGLEntries(po.getGeneralLedgerPendingEntries());
1069            LOG.debug("generateEntriesReopenPurchaseOrder() gl entries created; exit method");
1070        }
1071        LOG.debug("generateEntriesReopenPurchaseOrder() no gl entries created because the amount is 0; exit method");
1072    }
1073
1074    /**
1075     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesVoidPurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1076     */
1077    public void generateEntriesVoidPurchaseOrder(PurchaseOrderDocument po) {
1078        LOG.debug("generateEntriesVoidPurchaseOrder() started");
1079
1080        // Set outstanding encumbered quantity/amount on items
1081        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
1082            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
1083
1084            String logItmNbr = "Item # " + item.getItemLineNumber();
1085
1086            if (!item.isItemActiveIndicator()) {
1087                continue;
1088            }
1089
1090            //just use the outstanding amount as recalculating here, particularly the item tax will cause
1091            //amounts to be over or under encumbered and the remaining encumbered amount should be unencumbered during a close
1092            if (LOG.isDebugEnabled()) {
1093                LOG.debug("generateEntriesVoidPurchaseOrder() " + logItmNbr + " Calculate based on amounts");
1094            }
1095            KualiDecimal itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
1096
1097            KualiDecimal accountTotal = ZERO;
1098            PurchaseOrderAccount lastAccount = null;
1099            if (itemAmount.compareTo(ZERO) != 0) {
1100                // Sort accounts
1101                Collections.sort((List) item.getSourceAccountingLines());
1102
1103                for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext(); ) {
1104                    PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
1105                    if (!acct.isEmpty()) {
1106                        KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
1107                        accountTotal = accountTotal.add(acctAmount);
1108                        acct.setAlternateAmountForGLEntryCreation(acctAmount);
1109                        lastAccount = acct;
1110                    }
1111                }
1112
1113                // account for rounding by adjusting last account as needed
1114                if (lastAccount != null) {
1115                    KualiDecimal difference = itemAmount.subtract(accountTotal);
1116                    if (LOG.isDebugEnabled()) {
1117                        LOG.debug("generateEntriesVoidPurchaseOrder() difference: " + logItmNbr + " " + difference);
1118                    }
1119
1120                    KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
1121                    if (ObjectUtils.isNotNull(amount)) {
1122                        lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
1123                    } else {
1124                        lastAccount.setAlternateAmountForGLEntryCreation(difference);
1125                    }
1126                }
1127
1128            }
1129        }// endfor
1130
1131        po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
1132        generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
1133        saveGLEntries(po.getGeneralLedgerPendingEntries());
1134        LOG.debug("generateEntriesVoidPurchaseOrder() gl entries created; exit method");
1135    }
1136
1137    /**
1138     * 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
1139     * the encumbrance values on the PO and saves the PO
1140     *
1141     * @param preq PREQ for invoice
1142     * @return List of accounting lines to use to create the pending general ledger entries
1143     */
1144    protected List<SourceAccountingLine> relieveEncumbrance(PaymentRequestDocument preq) {
1145        LOG.debug("relieveEncumbrance() started");
1146
1147        Map encumbranceAccountMap = new HashMap();
1148        PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(preq.getPurchaseOrderIdentifier());
1149
1150        // Get each item one by one
1151        for (Iterator items = preq.getItems().iterator(); items.hasNext(); ) {
1152            PaymentRequestItem preqItem = (PaymentRequestItem) items.next();
1153            PurchaseOrderItem poItem = getPoItem(po, preqItem.getItemLineNumber(), preqItem.getItemType());
1154
1155            boolean takeAll = false; // Set this true if we relieve the entire encumbrance
1156            KualiDecimal itemDisEncumber = null; // Amount to disencumber for this item
1157
1158            String logItmNbr = "Item # " + preqItem.getItemLineNumber();
1159            if (LOG.isDebugEnabled()) {
1160                LOG.debug("relieveEncumbrance() " + logItmNbr);
1161            }
1162
1163            // If there isn't a PO item or the extended price is 0, we don't need encumbrances
1164            if (poItem == null) {
1165                if (LOG.isDebugEnabled()) {
1166                    LOG.debug("relieveEncumbrance() " + logItmNbr + " No encumbrances required because po item is null");
1167                }
1168            } else {
1169                final KualiDecimal preqItemTotalAmount = (preqItem.getTotalAmount() == null) ? KualiDecimal.ZERO : preqItem.getTotalAmount();
1170                if (ZERO.compareTo(preqItemTotalAmount) == 0) {
1171                    /*
1172                     * This is a specialized case where PREQ item being processed must adjust the PO item's outstanding encumbered
1173                     * quantity. This kind of scenario is mostly seen on warranty type items. The following must be true to do this:
1174                     * PREQ item Extended Price must be ZERO, PREQ item invoice quantity must be not empty and not ZERO, and PO item
1175                     * is quantity based PO item unit cost is ZERO
1176                     */
1177                    if (LOG.isDebugEnabled()) {
1178                        LOG.debug("relieveEncumbrance() " + logItmNbr + " No GL encumbrances required because extended price is ZERO");
1179                    }
1180                    if ((poItem.getItemQuantity() != null) && ((BigDecimal.ZERO.compareTo(poItem.getItemUnitPrice())) == 0)) {
1181                        // po has order quantity and unit price is ZERO... reduce outstanding encumbered quantity
1182                        if (LOG.isDebugEnabled()) {
1183                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate po oustanding encumbrance");
1184                        }
1185
1186                        // Do encumbrance calculations based on quantity
1187                        if ((preqItem.getItemQuantity() != null) && ((ZERO.compareTo(preqItem.getItemQuantity())) != 0)) {
1188                            KualiDecimal invoiceQuantity = preqItem.getItemQuantity();
1189                            KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1190
1191                            KualiDecimal encumbranceQuantity;
1192                            if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
1193                                // We bought more than the quantity on the PO
1194                                if (LOG.isDebugEnabled()) {
1195                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1196                                }
1197                                encumbranceQuantity = outstandingEncumberedQuantity;
1198                                poItem.setItemOutstandingEncumberedQuantity(ZERO);
1199                            } else {
1200                                encumbranceQuantity = invoiceQuantity;
1201                                poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
1202                                if (LOG.isDebugEnabled()) {
1203                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " adjusting oustanding encunbrance qty - encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
1204                                }
1205                            }
1206
1207                            if (poItem.getItemInvoicedTotalQuantity() == null) {
1208                                poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
1209                            } else {
1210                                poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
1211                            }
1212                        }
1213                    }
1214
1215
1216                } else {
1217                    if (LOG.isDebugEnabled()) {
1218                        LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
1219                    }
1220
1221                    // Do we calculate the encumbrance amount based on quantity or amount?
1222                    if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1223                        if (LOG.isDebugEnabled()) {
1224                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
1225                        }
1226
1227                        // Do encumbrance calculations based on quantity
1228                        KualiDecimal invoiceQuantity = preqItem.getItemQuantity() == null ? ZERO : preqItem.getItemQuantity();
1229                        KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1230
1231                        KualiDecimal encumbranceQuantity;
1232
1233                        if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
1234                            // We bought more than the quantity on the PO
1235                            if (LOG.isDebugEnabled()) {
1236                                LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1237                            }
1238                            encumbranceQuantity = outstandingEncumberedQuantity;
1239                            poItem.setItemOutstandingEncumberedQuantity(ZERO);
1240                            takeAll = true;
1241                        }
1242                        else if (outstandingEncumberedQuantity.isLessEqual(new KualiDecimal(0))) {
1243                            // We bought more than the quantity on the PO
1244                            //  if (LOG.isDebugEnabled()) {
1245                            LOG.info("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1246                            // }
1247                            encumbranceQuantity = poItem.getItemQuantity();
1248                            poItem.setItemOutstandingEncumberedQuantity(ZERO);
1249                            takeAll = true;
1250                        }
1251
1252                        else {
1253                            encumbranceQuantity = invoiceQuantity;
1254                            poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
1255                            if (ZERO.compareTo(poItem.getItemOutstandingEncumberedQuantity()) == 0) {
1256                                takeAll = true;
1257                            }
1258                            if (LOG.isDebugEnabled()) {
1259                                LOG.debug("relieveEncumbrance() " + logItmNbr + " encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
1260                            }
1261                        }
1262
1263                        if (poItem.getItemInvoicedTotalQuantity() == null) {
1264                            poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
1265                        } else {
1266                            poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
1267                        }
1268
1269                        itemDisEncumber = new KualiDecimal(encumbranceQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
1270
1271                        //add tax for encumbrance
1272                        KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
1273                        KualiDecimal encumbranceTaxAmount = encumbranceQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
1274                        itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount);
1275                    } else {
1276                        if (LOG.isDebugEnabled()) {
1277                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
1278                        }
1279
1280                        // Do encumbrance calculations based on amount only
1281                        if ((poItem.getItemOutstandingEncumberedAmount().bigDecimalValue().signum() == -1) && (preqItemTotalAmount.bigDecimalValue().signum() == -1)) {
1282                            if (LOG.isDebugEnabled()) {
1283                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is negative: " + poItem.getItemOutstandingEncumberedAmount());
1284                            }
1285                            if (preqItemTotalAmount.compareTo(poItem.getItemOutstandingEncumberedAmount()) >= 0) {
1286                                // extended price is equal to or greater than outstanding encumbered
1287                                itemDisEncumber = preqItemTotalAmount;
1288                            }
1289                            else if (poItem.getItemOutstandingEncumberedAmount().isLessEqual(new KualiDecimal(0))) {
1290                                // We bought more than the quantity on the PO
1291                                //  if (LOG.isDebugEnabled()) {
1292                                LOG.info("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1293                                //  }
1294                                itemDisEncumber = poItem.getItemQuantity().multiply(new KualiDecimal(poItem.getItemUnitPrice()));
1295                                poItem.setItemOutstandingEncumberedQuantity(ZERO);
1296                                takeAll = true;
1297                            }
1298
1299                            else {
1300                                // extended price is less than outstanding encumbered
1301                                takeAll = true;
1302                                itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
1303                            }
1304                        } else {
1305                            if (LOG.isDebugEnabled()) {
1306                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is positive or ZERO: " + poItem.getItemOutstandingEncumberedAmount());
1307                            }
1308                            if (poItem.getItemOutstandingEncumberedAmount().compareTo(preqItemTotalAmount) >= 0) {
1309                                // outstanding amount is equal to or greater than extended price
1310                                itemDisEncumber = preqItemTotalAmount;
1311                            } else {
1312                                // outstanding amount is less than extended price
1313                                takeAll = true;
1314                                itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
1315                            }
1316                        }
1317                    }
1318
1319                    if (LOG.isDebugEnabled()) {
1320                        LOG.debug("relieveEncumbrance() " + logItmNbr + " Amount to disencumber: " + itemDisEncumber);
1321                    }
1322
1323                    KualiDecimal newOutstandingEncumberedAmount = new KualiDecimal(0);
1324                    if (poItem.getItemOutstandingEncumberedAmount().isLessEqual(new KualiDecimal(0))) {
1325                        newOutstandingEncumberedAmount = itemDisEncumber;
1326                    }
1327                    else {
1328                        newOutstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount().subtract(itemDisEncumber);
1329                    }
1330                    if (LOG.isDebugEnabled()) {
1331                        LOG.debug("relieveEncumbrance() " + logItmNbr + " New Outstanding Encumbered amount is : " + newOutstandingEncumberedAmount);
1332                    }
1333                    poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
1334
1335                    KualiDecimal newInvoicedTotalAmount = poItem.getItemInvoicedTotalAmount().add(preqItemTotalAmount);
1336                    if (LOG.isDebugEnabled()) {
1337                        LOG.debug("relieveEncumbrance() " + logItmNbr + " New Invoiced Total Amount is: " + newInvoicedTotalAmount);
1338                    }
1339                    poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
1340
1341                    // Sort accounts
1342                    Collections.sort((List) poItem.getSourceAccountingLines());
1343
1344                    // make the list of accounts for the disencumbrance entry
1345                    PurchaseOrderAccount lastAccount = null;
1346                    KualiDecimal accountTotal = ZERO;
1347                    for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
1348                        PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
1349                        if (!account.isEmpty()) {
1350                            KualiDecimal encumbranceAmount = null;
1351                            SourceAccountingLine acctString = account.generateSourceAccountingLine();
1352                            if (takeAll) {
1353                                // fully paid; remove remaining encumbrance
1354                                encumbranceAmount = account.getItemAccountOutstandingEncumbranceAmount();
1355                                account.setItemAccountOutstandingEncumbranceAmount(ZERO);
1356                                if (LOG.isDebugEnabled()) {
1357                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " take all");
1358                                }
1359                            } else {
1360                                // amount = item disencumber * account percent / 100
1361                                encumbranceAmount = itemDisEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
1362
1363                                account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().subtract(encumbranceAmount));
1364
1365                                // For rounding check at the end
1366                                accountTotal = accountTotal.add(encumbranceAmount);
1367
1368                                // If we are zeroing out the encumbrance, we don't need to adjust for rounding
1369                                if (!takeAll) {
1370                                    lastAccount = account;
1371                                }
1372                            }
1373
1374                            if (LOG.isDebugEnabled()) {
1375                                LOG.debug("relieveEncumbrance() " + logItmNbr + " " + acctString + " = " + encumbranceAmount);
1376                            }
1377                            if (ObjectUtils.isNull(encumbranceAccountMap.get(acctString))) {
1378                                encumbranceAccountMap.put(acctString, encumbranceAmount);
1379                            } else {
1380                                KualiDecimal amt = (KualiDecimal) encumbranceAccountMap.get(acctString);
1381                                encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount));
1382                            }
1383
1384                        }
1385                    }
1386
1387                    // account for rounding by adjusting last account as needed
1388                    if (lastAccount != null) {
1389                        KualiDecimal difference = itemDisEncumber.subtract(accountTotal);
1390                        if (LOG.isDebugEnabled()) {
1391                            LOG.debug("relieveEncumbrance() difference: " + logItmNbr + " " + difference);
1392                        }
1393
1394                        SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
1395                        KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1396                        if (ObjectUtils.isNull(amount)) {
1397                            encumbranceAccountMap.put(acctString, difference);
1398                        } else {
1399                            encumbranceAccountMap.put(acctString, amount.add(difference));
1400                        }
1401
1402                        lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().subtract(difference));
1403                    }
1404                }
1405            }
1406        }// endfor
1407
1408        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
1409
1410        List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
1411        for (Iterator iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
1412            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
1413            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1414            if (amount.doubleValue() != 0) {
1415                acctString.setAmount(amount);
1416                encumbranceAccounts.add(acctString);
1417            }
1418        }
1419        //SpringContext.getBean(BusinessObjectService.class).save(po);
1420        return encumbranceAccounts;
1421    }
1422
1423
1424    /**
1425     * 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
1426     * the encumbrance values on the PO and saves the PO
1427     *
1428     * @param inv inv for InvoiceDocument
1429     * @return List of accounting lines to use to create the pending general ledger entries
1430     */
1431    protected List<SourceAccountingLine> relieveEncumbrance(InvoiceDocument inv) {
1432        LOG.debug("relieveEncumbrance() started");
1433
1434        Map encumbranceAccountMap = new HashMap();
1435        // PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(inv.getPurchaseOrderIdentifier());
1436        for (PurchaseOrderDocument po : inv.getPurchaseOrderDocuments()) {
1437            // Get each item one by one
1438            for (Iterator items = inv.getItems().iterator(); items.hasNext(); ) {
1439                InvoiceItem invItem = (InvoiceItem) items.next();
1440                PurchaseOrderItem poItem = getPoItem(po, invItem.getItemLineNumber(), invItem.getItemType());
1441
1442                boolean takeAll = false; // Set this true if we relieve the entire encumbrance
1443                KualiDecimal itemDisEncumber = null; // Amount to disencumber for this item
1444
1445                String logItmNbr = "Item # " + invItem.getItemLineNumber();
1446                if (LOG.isDebugEnabled()) {
1447                    LOG.debug("relieveEncumbrance() " + logItmNbr);
1448                }
1449
1450                // If there isn't a PO item or the extended price is 0, we don't need encumbrances
1451                if (poItem == null) {
1452                    if (LOG.isDebugEnabled()) {
1453                        LOG.debug("relieveEncumbrance() " + logItmNbr + " No encumbrances required because po item is null");
1454                    }
1455                } else {
1456                    final KualiDecimal preqItemTotalAmount = (invItem.getTotalAmount() == null) ? KualiDecimal.ZERO : invItem.getTotalAmount();
1457                    if (ZERO.compareTo(preqItemTotalAmount) == 0) {
1458                    /*
1459                     * This is a specialized case where PREQ item being processed must adjust the PO item's outstanding encumbered
1460                     * quantity. This kind of scenario is mostly seen on warranty type items. The following must be true to do this:
1461                     * PREQ item Extended Price must be ZERO, PREQ item invoice quantity must be not empty and not ZERO, and PO item
1462                     * is quantity based PO item unit cost is ZERO
1463                     */
1464                        if (LOG.isDebugEnabled()) {
1465                            LOG.debug("relieveEncumbrance() " + logItmNbr + " No GL encumbrances required because extended price is ZERO");
1466                        }
1467                        if ((poItem.getItemQuantity() != null) && ((BigDecimal.ZERO.compareTo(poItem.getItemUnitPrice())) == 0)) {
1468                            // po has order quantity and unit price is ZERO... reduce outstanding encumbered quantity
1469                            if (LOG.isDebugEnabled()) {
1470                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate po oustanding encumbrance");
1471                            }
1472
1473                            // Do encumbrance calculations based on quantity
1474                            if ((invItem.getItemQuantity() != null) && ((ZERO.compareTo(invItem.getItemQuantity())) != 0)) {
1475                                KualiDecimal invoiceQuantity = invItem.getItemQuantity();
1476                                KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1477
1478                                KualiDecimal encumbranceQuantity;
1479                                if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
1480                                    // We bought more than the quantity on the PO
1481                                    if (LOG.isDebugEnabled()) {
1482                                        LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1483                                    }
1484                                    encumbranceQuantity = outstandingEncumberedQuantity;
1485                                    poItem.setItemOutstandingEncumberedQuantity(ZERO);
1486                                } else {
1487                                    encumbranceQuantity = invoiceQuantity;
1488                                    poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
1489                                    if (LOG.isDebugEnabled()) {
1490                                        LOG.debug("relieveEncumbrance() " + logItmNbr + " adjusting oustanding encunbrance qty - encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
1491                                    }
1492                                }
1493
1494                                if (poItem.getItemInvoicedTotalQuantity() == null) {
1495                                    poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
1496                                } else {
1497                                    poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
1498                                }
1499                            }
1500                        }
1501
1502
1503                    } else {
1504                        if (LOG.isDebugEnabled()) {
1505                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
1506                        }
1507
1508                        // Do we calculate the encumbrance amount based on quantity or amount?
1509                        if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1510                            if (LOG.isDebugEnabled()) {
1511                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
1512                            }
1513
1514                            // Do encumbrance calculations based on quantity
1515                            KualiDecimal invoiceQuantity = invItem.getItemQuantity() == null ? ZERO : invItem.getItemQuantity();
1516                            KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1517
1518                            KualiDecimal encumbranceQuantity;
1519
1520                            if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
1521                                // We bought more than the quantity on the PO
1522                                if (LOG.isDebugEnabled()) {
1523                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1524                                }
1525                                encumbranceQuantity = outstandingEncumberedQuantity;
1526                                poItem.setItemOutstandingEncumberedQuantity(ZERO);
1527                                takeAll = true;
1528                            } else {
1529                                encumbranceQuantity = invoiceQuantity;
1530                                poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
1531                                if (ZERO.compareTo(poItem.getItemOutstandingEncumberedQuantity()) == 0) {
1532                                    takeAll = true;
1533                                }
1534                                if (LOG.isDebugEnabled()) {
1535                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
1536                                }
1537                            }
1538
1539                            if (poItem.getItemInvoicedTotalQuantity() == null) {
1540                                poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
1541                            } else {
1542                                poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
1543                            }
1544
1545                            itemDisEncumber = new KualiDecimal(encumbranceQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
1546
1547                            //add tax for encumbrance
1548                            KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
1549                            KualiDecimal encumbranceTaxAmount = encumbranceQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
1550                            itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount);
1551                        } else {
1552                            if (LOG.isDebugEnabled()) {
1553                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
1554                            }
1555
1556                            // Do encumbrance calculations based on amount only
1557                            if ((poItem.getItemOutstandingEncumberedAmount().bigDecimalValue().signum() == -1) && (preqItemTotalAmount.bigDecimalValue().signum() == -1)) {
1558                                if (LOG.isDebugEnabled()) {
1559                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is negative: " + poItem.getItemOutstandingEncumberedAmount());
1560                                }
1561                                if (preqItemTotalAmount.compareTo(poItem.getItemOutstandingEncumberedAmount()) >= 0) {
1562                                    // extended price is equal to or greater than outstanding encumbered
1563                                    itemDisEncumber = preqItemTotalAmount;
1564                                } else {
1565                                    // extended price is less than outstanding encumbered
1566                                    takeAll = true;
1567                                    itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
1568                                }
1569                            } else {
1570                                if (LOG.isDebugEnabled()) {
1571                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is positive or ZERO: " + poItem.getItemOutstandingEncumberedAmount());
1572                                }
1573                                if (poItem.getItemOutstandingEncumberedAmount().compareTo(preqItemTotalAmount) >= 0) {
1574                                    // outstanding amount is equal to or greater than extended price
1575                                    itemDisEncumber = preqItemTotalAmount;
1576                                } else {
1577                                    // outstanding amount is less than extended price
1578                                    takeAll = true;
1579                                    itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
1580                                }
1581                            }
1582                        }
1583
1584                        if (LOG.isDebugEnabled()) {
1585                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Amount to disencumber: " + itemDisEncumber);
1586                        }
1587
1588                        KualiDecimal newOutstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount().subtract(itemDisEncumber);
1589                        if (LOG.isDebugEnabled()) {
1590                            LOG.debug("relieveEncumbrance() " + logItmNbr + " New Outstanding Encumbered amount is : " + newOutstandingEncumberedAmount);
1591                        }
1592                        poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
1593
1594                        KualiDecimal newInvoicedTotalAmount = poItem.getItemInvoicedTotalAmount().add(preqItemTotalAmount);
1595                        if (LOG.isDebugEnabled()) {
1596                            LOG.debug("relieveEncumbrance() " + logItmNbr + " New Invoiced Total Amount is: " + newInvoicedTotalAmount);
1597                        }
1598                        poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
1599
1600                        // Sort accounts
1601                        Collections.sort((List) poItem.getSourceAccountingLines());
1602
1603                        // make the list of accounts for the disencumbrance entry
1604                        PurchaseOrderAccount lastAccount = null;
1605                        KualiDecimal accountTotal = ZERO;
1606                        for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
1607                            PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
1608                            if (!account.isEmpty()) {
1609                                KualiDecimal encumbranceAmount = null;
1610                                SourceAccountingLine acctString = account.generateSourceAccountingLine();
1611                                if (takeAll) {
1612                                    // fully paid; remove remaining encumbrance
1613                                    encumbranceAmount = account.getItemAccountOutstandingEncumbranceAmount();
1614                                    account.setItemAccountOutstandingEncumbranceAmount(ZERO);
1615                                    if (LOG.isDebugEnabled()) {
1616                                        LOG.debug("relieveEncumbrance() " + logItmNbr + " take all");
1617                                    }
1618                                } else {
1619                                    // amount = item disencumber * account percent / 100
1620                                    encumbranceAmount = itemDisEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
1621
1622                                    account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().subtract(encumbranceAmount));
1623
1624                                    // For rounding check at the end
1625                                    accountTotal = accountTotal.add(encumbranceAmount);
1626
1627                                    // If we are zeroing out the encumbrance, we don't need to adjust for rounding
1628                                    if (!takeAll) {
1629                                        lastAccount = account;
1630                                    }
1631                                }
1632
1633                                if (LOG.isDebugEnabled()) {
1634                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " " + acctString + " = " + encumbranceAmount);
1635                                }
1636                                if (ObjectUtils.isNull(encumbranceAccountMap.get(acctString))) {
1637                                    encumbranceAccountMap.put(acctString, encumbranceAmount);
1638                                } else {
1639                                    KualiDecimal amt = (KualiDecimal) encumbranceAccountMap.get(acctString);
1640                                    encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount));
1641                                }
1642
1643                            }
1644                        }
1645
1646                        // account for rounding by adjusting last account as needed
1647                        if (lastAccount != null) {
1648                            KualiDecimal difference = itemDisEncumber.subtract(accountTotal);
1649                            if (LOG.isDebugEnabled()) {
1650                                LOG.debug("relieveEncumbrance() difference: " + logItmNbr + " " + difference);
1651                            }
1652
1653                            SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
1654                            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1655                            if (ObjectUtils.isNull(amount)) {
1656                                encumbranceAccountMap.put(acctString, difference);
1657                            } else {
1658                                encumbranceAccountMap.put(acctString, amount.add(difference));
1659                            }
1660
1661                            lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().subtract(difference));
1662                        }
1663                    }
1664                }
1665            }// endfor
1666        }
1667
1668        List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
1669        for (Iterator iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
1670            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
1671            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1672            if (amount.doubleValue() != 0) {
1673                acctString.setAmount(amount);
1674                encumbranceAccounts.add(acctString);
1675            }
1676        }
1677
1678        // SpringContext.getBean(BusinessObjectService.class).save(po);
1679        return encumbranceAccounts;
1680    }
1681
1682
1683    /**
1684     * 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
1685     * encumbrance values on the PO and saves the PO
1686     *
1687     * @param preq PREQ for invoice
1688     * @return List of accounting lines to use to create the pending general ledger entries
1689     */
1690    protected List<SourceAccountingLine> reencumberEncumbrance(PaymentRequestDocument preq) {
1691        LOG.debug("reencumberEncumbrance() started");
1692
1693        PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(preq.getPurchaseOrderIdentifier());
1694        Map encumbranceAccountMap = new HashMap();
1695
1696        // Get each item one by one
1697        for (Iterator items = preq.getItems().iterator(); items.hasNext(); ) {
1698            PaymentRequestItem payRequestItem = (PaymentRequestItem) items.next();
1699            PurchaseOrderItem poItem = getPoItem(po, payRequestItem.getItemLineNumber(), payRequestItem.getItemType());
1700
1701            KualiDecimal itemReEncumber = null; // Amount to reencumber for this item
1702
1703            String logItmNbr = "Item # " + payRequestItem.getItemLineNumber();
1704            if (LOG.isDebugEnabled()) {
1705                LOG.debug("reencumberEncumbrance() " + logItmNbr);
1706            }
1707
1708            // If there isn't a PO item or the total amount is 0, we don't need encumbrances
1709            final KualiDecimal preqItemTotalAmount = (payRequestItem.getTotalAmount() == null) ? KualiDecimal.ZERO : payRequestItem.getTotalAmount();
1710            if ((poItem == null) || (preqItemTotalAmount.doubleValue() == 0)) {
1711                if (LOG.isDebugEnabled()) {
1712                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " No encumbrances required");
1713                }
1714            } else {
1715                if (LOG.isDebugEnabled()) {
1716                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
1717                }
1718
1719                // Do we calculate the encumbrance amount based on quantity or amount?
1720                if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1721                    if (LOG.isDebugEnabled()) {
1722                        LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
1723                    }
1724
1725                    // Do disencumbrance calculations based on quantity
1726                    KualiDecimal preqQuantity = payRequestItem.getItemQuantity() == null ? ZERO : payRequestItem.getItemQuantity();
1727                    KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1728                    KualiDecimal invoicedTotal = poItem.getItemInvoicedTotalQuantity() == null ? ZERO : poItem.getItemInvoicedTotalQuantity();
1729
1730                    poItem.setItemInvoicedTotalQuantity(invoicedTotal.subtract(preqQuantity));
1731                    poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.add(preqQuantity));
1732
1733                    //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
1734                    itemReEncumber = new KualiDecimal(preqQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
1735
1736                    //add tax for encumbrance
1737                    KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
1738                    KualiDecimal encumbranceTaxAmount = preqQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
1739                    itemReEncumber = itemReEncumber.add(encumbranceTaxAmount);
1740
1741                } else {
1742                    if (LOG.isDebugEnabled()) {
1743                        LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
1744                    }
1745
1746                    itemReEncumber = preqItemTotalAmount;
1747                    // if re-encumber amount is more than original PO ordered amount... do not exceed ordered amount
1748                    // this prevents negative encumbrance
1749                    if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() < 0)) {
1750                        // po item extended cost is negative
1751                        if ((poItem.getTotalAmount().compareTo(itemReEncumber)) > 0) {
1752                            itemReEncumber = poItem.getTotalAmount();
1753                        }
1754                    } else if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() >= 0)) {
1755                        // po item extended cost is positive
1756                        if ((poItem.getTotalAmount().compareTo(itemReEncumber)) < 0) {
1757                            itemReEncumber = poItem.getTotalAmount();
1758                        }
1759                    }
1760                }
1761
1762                if (LOG.isDebugEnabled()) {
1763                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " Amount to reencumber: " + itemReEncumber);
1764                }
1765
1766                KualiDecimal outstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount() == null ? ZERO : poItem.getItemOutstandingEncumberedAmount();
1767                if (LOG.isDebugEnabled()) {
1768                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Outstanding Encumbrance Amount set to: " + outstandingEncumberedAmount);
1769                }
1770                KualiDecimal newOutstandingEncumberedAmount = outstandingEncumberedAmount.add(itemReEncumber);
1771                if (LOG.isDebugEnabled()) {
1772                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Outstanding Encumbrance Amount to set: " + newOutstandingEncumberedAmount);
1773                }
1774                poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
1775
1776                KualiDecimal invoicedTotalAmount = poItem.getItemInvoicedTotalAmount() == null ? ZERO : poItem.getItemInvoicedTotalAmount();
1777                if (LOG.isDebugEnabled()) {
1778                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Invoiced Total Amount set to: " + invoicedTotalAmount);
1779                }
1780                KualiDecimal newInvoicedTotalAmount = invoicedTotalAmount.subtract(preqItemTotalAmount);
1781                if (LOG.isDebugEnabled()) {
1782                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Invoiced Total Amount to set: " + newInvoicedTotalAmount);
1783                }
1784                poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
1785
1786                // make the list of accounts for the reencumbrance entry
1787                PurchaseOrderAccount lastAccount = null;
1788                KualiDecimal accountTotal = ZERO;
1789
1790                // Sort accounts
1791                Collections.sort((List) poItem.getSourceAccountingLines());
1792
1793                for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
1794                    PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
1795                    if (!account.isEmpty()) {
1796                        SourceAccountingLine acctString = account.generateSourceAccountingLine();
1797
1798                        // amount = item reencumber * account percent / 100
1799                        KualiDecimal reencumbranceAmount = itemReEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
1800
1801                        account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().add(reencumbranceAmount));
1802
1803                        // For rounding check at the end
1804                        accountTotal = accountTotal.add(reencumbranceAmount);
1805
1806                        lastAccount = account;
1807
1808                        if (LOG.isDebugEnabled()) {
1809                            LOG.debug("reencumberEncumbrance() " + logItmNbr + " " + acctString + " = " + reencumbranceAmount);
1810                        }
1811                        if (encumbranceAccountMap.containsKey(acctString)) {
1812                            KualiDecimal currentAmount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1813                            encumbranceAccountMap.put(acctString, reencumbranceAmount.add(currentAmount));
1814                        } else {
1815                            encumbranceAccountMap.put(acctString, reencumbranceAmount);
1816                        }
1817                    }
1818                }
1819
1820                // account for rounding by adjusting last account as needed
1821                if (lastAccount != null) {
1822                    KualiDecimal difference = itemReEncumber.subtract(accountTotal);
1823                    if (LOG.isDebugEnabled()) {
1824                        LOG.debug("reencumberEncumbrance() difference: " + logItmNbr + " " + difference);
1825                    }
1826
1827                    SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
1828                    KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1829                    if (amount == null) {
1830                        encumbranceAccountMap.put(acctString, difference);
1831                    } else {
1832                        encumbranceAccountMap.put(acctString, amount.add(difference));
1833                    }
1834                    lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference));
1835                }
1836            }
1837        }
1838
1839        //SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
1840
1841        //MSU Contribution OLEMI-8639 DTT-4009 OLECNTRB-966
1842        //  SpringContext.getBean(BusinessObjectService.class).save(po);
1843
1844        List<SourceAccountingLine> encumbranceAccounts = new ArrayList<SourceAccountingLine>();
1845        for (Iterator<SourceAccountingLine> iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
1846            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
1847            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1848            if (amount.doubleValue() != 0) {
1849                acctString.setAmount(amount);
1850                encumbranceAccounts.add(acctString);
1851            }
1852        }
1853
1854        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
1855        return encumbranceAccounts;
1856    }
1857
1858
1859    protected List<SourceAccountingLine> reencumberEncumbrance(InvoiceDocument prqs) {
1860        LOG.debug("reencumberEncumbrance() started");
1861
1862        PurchaseOrderDocument po = null;
1863                //purchaseOrderService.getCurrentPurchaseOrder(prqs.getPurchaseOrderIdentifier());
1864        Map encumbranceAccountMap = new HashMap();
1865
1866        // Get each item one by one
1867        for (Iterator items = prqs.getItems().iterator(); items.hasNext(); ) {
1868            InvoiceItem invItem = (InvoiceItem) items.next();
1869            po = purchaseOrderService.getCurrentPurchaseOrder(invItem.getPurchaseOrderIdentifier());
1870            PurchaseOrderItem poItem = null;
1871            if(po != null) {
1872                 poItem = getPoItem(po, invItem.getItemLineNumber(), invItem.getItemType());
1873            }
1874
1875            KualiDecimal itemReEncumber = null; // Amount to reencumber for this item
1876
1877            String logItmNbr = "Item # " + invItem.getItemLineNumber();
1878            if (LOG.isDebugEnabled()) {
1879                LOG.debug("reencumberEncumbrance() " + logItmNbr);
1880            }
1881
1882            // If there isn't a PO item or the total amount is 0, we don't need encumbrances
1883            final KualiDecimal preqItemTotalAmount = (invItem.getTotalAmount() == null) ? KualiDecimal.ZERO : invItem.getTotalAmount();
1884            if ((poItem == null) || (preqItemTotalAmount.doubleValue() == 0)) {
1885                if (LOG.isDebugEnabled()) {
1886                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " No encumbrances required");
1887                }
1888            } else {
1889                if (LOG.isDebugEnabled()) {
1890                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
1891                }
1892
1893                // Do we calculate the encumbrance amount based on quantity or amount?
1894                if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1895                    if (LOG.isDebugEnabled()) {
1896                        LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
1897                    }
1898
1899                    // Do disencumbrance calculations based on quantity
1900                    KualiDecimal preqQuantity = invItem.getItemQuantity() == null ? ZERO : invItem.getItemQuantity();
1901                    KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1902                    KualiDecimal invoicedTotal = poItem.getItemInvoicedTotalQuantity() == null ? ZERO : poItem.getItemInvoicedTotalQuantity();
1903
1904                    poItem.setItemInvoicedTotalQuantity(invoicedTotal.subtract(preqQuantity));
1905                    poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.add(preqQuantity));
1906
1907                    //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
1908                    itemReEncumber = new KualiDecimal(preqQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
1909
1910                    //add tax for encumbrance
1911                    KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
1912                    KualiDecimal encumbranceTaxAmount = preqQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
1913                    itemReEncumber = itemReEncumber.add(encumbranceTaxAmount);
1914
1915                } else {
1916                    if (LOG.isDebugEnabled()) {
1917                        LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
1918                    }
1919
1920                    itemReEncumber = preqItemTotalAmount;
1921                    // if re-encumber amount is more than original PO ordered amount... do not exceed ordered amount
1922                    // this prevents negative encumbrance
1923                    if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() < 0)) {
1924                        // po item extended cost is negative
1925                        if ((poItem.getTotalAmount().compareTo(itemReEncumber)) > 0) {
1926                            itemReEncumber = poItem.getTotalAmount();
1927                        }
1928                    } else if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() >= 0)) {
1929                        // po item extended cost is positive
1930                        if ((poItem.getTotalAmount().compareTo(itemReEncumber)) < 0) {
1931                            itemReEncumber = poItem.getTotalAmount();
1932                        }
1933                    }
1934                }
1935
1936                if (LOG.isDebugEnabled()) {
1937                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " Amount to reencumber: " + itemReEncumber);
1938                }
1939
1940                KualiDecimal outstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount() == null ? ZERO : poItem.getItemOutstandingEncumberedAmount();
1941                if (LOG.isDebugEnabled()) {
1942                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Outstanding Encumbrance Amount set to: " + outstandingEncumberedAmount);
1943                }
1944                KualiDecimal newOutstandingEncumberedAmount = outstandingEncumberedAmount.add(itemReEncumber);
1945                if (LOG.isDebugEnabled()) {
1946                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Outstanding Encumbrance Amount to set: " + newOutstandingEncumberedAmount);
1947                }
1948                poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
1949
1950                KualiDecimal invoicedTotalAmount = poItem.getItemInvoicedTotalAmount() == null ? ZERO : poItem.getItemInvoicedTotalAmount();
1951                if (LOG.isDebugEnabled()) {
1952                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Invoiced Total Amount set to: " + invoicedTotalAmount);
1953                }
1954                KualiDecimal newInvoicedTotalAmount = invoicedTotalAmount.subtract(preqItemTotalAmount);
1955                if (LOG.isDebugEnabled()) {
1956                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Invoiced Total Amount to set: " + newInvoicedTotalAmount);
1957                }
1958                poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
1959
1960                // make the list of accounts for the reencumbrance entry
1961                PurchaseOrderAccount lastAccount = null;
1962                KualiDecimal accountTotal = ZERO;
1963
1964                // Sort accounts
1965                Collections.sort((List) poItem.getSourceAccountingLines());
1966
1967                for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
1968                    PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
1969                    if (!account.isEmpty()) {
1970                        SourceAccountingLine acctString = account.generateSourceAccountingLine();
1971
1972                        // amount = item reencumber * account percent / 100
1973                        KualiDecimal reencumbranceAmount = itemReEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
1974
1975                        account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().add(reencumbranceAmount));
1976
1977                        // For rounding check at the end
1978                        accountTotal = accountTotal.add(reencumbranceAmount);
1979
1980                        lastAccount = account;
1981
1982                        if (LOG.isDebugEnabled()) {
1983                            LOG.debug("reencumberEncumbrance() " + logItmNbr + " " + acctString + " = " + reencumbranceAmount);
1984                        }
1985                        if (encumbranceAccountMap.containsKey(acctString)) {
1986                            KualiDecimal currentAmount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1987                            encumbranceAccountMap.put(acctString, reencumbranceAmount.add(currentAmount));
1988                        } else {
1989                            encumbranceAccountMap.put(acctString, reencumbranceAmount);
1990                        }
1991                    }
1992                }
1993
1994                // account for rounding by adjusting last account as needed
1995                if (lastAccount != null) {
1996                    KualiDecimal difference = itemReEncumber.subtract(accountTotal);
1997                    if (LOG.isDebugEnabled()) {
1998                        LOG.debug("reencumberEncumbrance() difference: " + logItmNbr + " " + difference);
1999                    }
2000
2001                    SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
2002                    KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
2003                    if (amount == null) {
2004                        encumbranceAccountMap.put(acctString, difference);
2005                    } else {
2006                        encumbranceAccountMap.put(acctString, amount.add(difference));
2007                    }
2008                    lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference));
2009                }
2010            }
2011        }
2012
2013        //SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
2014
2015        //MSU Contribution OLEMI-8639 DTT-4009 OLECNTRB-966
2016        // SpringContext.getBean(BusinessObjectService.class).save(po);
2017
2018        List<SourceAccountingLine> encumbranceAccounts = new ArrayList<SourceAccountingLine>();
2019        for (Iterator<SourceAccountingLine> iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
2020            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
2021            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
2022            if (amount.doubleValue() != 0) {
2023                acctString.setAmount(amount);
2024                encumbranceAccounts.add(acctString);
2025            }
2026        }
2027        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
2028        return encumbranceAccounts;
2029    }
2030
2031
2032    /**
2033     * 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
2034     * encumbrance values on the PO and saves the PO
2035     *
2036     * @param cm Credit Memo document
2037     * @param po Purchase Order document modify encumbrances
2038     * @return List of accounting lines to use to create the pending general ledger entries
2039     */
2040    protected List<SourceAccountingLine> getCreditMemoEncumbrance(VendorCreditMemoDocument cm, PurchaseOrderDocument po, boolean cancel) {
2041        LOG.debug("getCreditMemoEncumbrance() started");
2042
2043        if (ObjectUtils.isNull(po)) {
2044            return null;
2045        }
2046
2047        if (cancel) {
2048            LOG.debug("getCreditMemoEncumbrance() Receiving items back from vendor (cancelled CM)");
2049        } else {
2050            LOG.debug("getCreditMemoEncumbrance() Returning items to vendor");
2051        }
2052
2053        Map encumbranceAccountMap = new HashMap();
2054
2055        // Get each item one by one
2056        for (Iterator items = cm.getItems().iterator(); items.hasNext(); ) {
2057            CreditMemoItem cmItem = (CreditMemoItem) items.next();
2058            PurchaseOrderItem poItem = getPoItem(po, cmItem.getItemLineNumber(), cmItem.getItemType());
2059
2060            KualiDecimal itemDisEncumber = null; // Amount to disencumber for this item
2061            KualiDecimal itemAlterInvoiceAmt = null; // Amount to alter the invoicedAmt on the PO item
2062
2063            String logItmNbr = "Item # " + cmItem.getItemLineNumber();
2064            if (LOG.isDebugEnabled()) {
2065                LOG.debug("getCreditMemoEncumbrance() " + logItmNbr);
2066            }
2067
2068            final KualiDecimal cmItemTotalAmount = (cmItem.getTotalAmount() == null) ? KualiDecimal.ZERO : cmItem.getTotalAmount();
2069            ;
2070            // If there isn't a PO item or the total amount is 0, we don't need encumbrances
2071            if ((poItem == null) || (cmItemTotalAmount == null) || (cmItemTotalAmount.doubleValue() == 0) ||
2072                    (cmItemTotalAmount!=null && !cmItemTotalAmount.isNegative())) {
2073                if (LOG.isDebugEnabled()) {
2074                    LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " No encumbrances required");
2075                }
2076            } else {
2077                if (LOG.isDebugEnabled()) {
2078                    LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
2079                }
2080
2081                // Do we calculate the encumbrance amount based on quantity or amount?
2082                if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
2083                    if (LOG.isDebugEnabled()) {
2084                        LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
2085                    }
2086
2087                    // Do encumbrance calculations based on quantity
2088                    KualiDecimal cmQuantity = cmItem.getItemQuantity() == null ? ZERO : cmItem.getItemQuantity();
2089
2090                    KualiDecimal encumbranceQuantityChange = calculateQuantityChange(cancel, poItem, cmQuantity);
2091
2092                    if (LOG.isDebugEnabled()) {
2093                        LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " encumbranceQtyChange " + encumbranceQuantityChange + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity() + " invoicedTotalQuantity " + poItem.getItemInvoicedTotalQuantity());
2094                    }
2095
2096                    //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
2097                    itemDisEncumber = new KualiDecimal(encumbranceQuantityChange.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
2098
2099                    //add tax for encumbrance
2100                    KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
2101                    KualiDecimal encumbranceTaxAmount = encumbranceQuantityChange.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
2102                    itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount);
2103
2104                    itemAlterInvoiceAmt = cmItemTotalAmount;
2105                    if (cancel) {
2106                        itemAlterInvoiceAmt = itemAlterInvoiceAmt.multiply(new KualiDecimal("-1"));
2107                    }
2108                } else {
2109                    if (LOG.isDebugEnabled()) {
2110                        LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
2111                    }
2112
2113                    // Do encumbrance calculations based on amount only
2114                    if (cancel) {
2115                        // Decrease encumbrance
2116                        itemDisEncumber = cmItemTotalAmount.multiply(new KualiDecimal("-1"));
2117
2118                        if (poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber).doubleValue() < 0) {
2119                            LOG.debug("getCreditMemoEncumbrance() Cancel overflow");
2120
2121                            itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
2122                        }
2123                    } else {
2124                        // Increase encumbrance
2125                        itemDisEncumber = cmItemTotalAmount;
2126
2127                        if (poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber).doubleValue() > poItem.getTotalAmount().doubleValue()) {
2128                            LOG.debug("getCreditMemoEncumbrance() Create overflow");
2129
2130                            itemDisEncumber = poItem.getTotalAmount().subtract(poItem.getItemOutstandingEncumberedAmount());
2131                        }
2132                    }
2133                    itemAlterInvoiceAmt = itemDisEncumber;
2134                }
2135
2136                // alter the encumbrance based on what was originally encumbered
2137                poItem.setItemOutstandingEncumberedAmount(poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber));
2138
2139                // alter the invoiced amt based on what was actually credited on the credit memo
2140                poItem.setItemInvoicedTotalAmount(poItem.getItemInvoicedTotalAmount().subtract(itemAlterInvoiceAmt));
2141                if (poItem.getItemInvoicedTotalAmount().compareTo(ZERO) < 0) {
2142                    poItem.setItemInvoicedTotalAmount(ZERO);
2143                }
2144
2145
2146                if (LOG.isDebugEnabled()) {
2147                    LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Amount to disencumber: " + itemDisEncumber);
2148                }
2149
2150                // Sort accounts
2151                Collections.sort((List) poItem.getSourceAccountingLines());
2152
2153                // make the list of accounts for the disencumbrance entry
2154                PurchaseOrderAccount lastAccount = null;
2155                KualiDecimal accountTotal = ZERO;
2156                // Collections.sort((List)poItem.getSourceAccountingLines());
2157                for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
2158                    PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
2159                    if (!account.isEmpty()) {
2160                        KualiDecimal encumbranceAmount = null;
2161
2162                        SourceAccountingLine acctString = account.generateSourceAccountingLine();
2163                        // amount = item disencumber * account percent / 100
2164                        encumbranceAmount = itemDisEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(new KualiDecimal(100));
2165
2166                        account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().add(encumbranceAmount));
2167
2168                        // For rounding check at the end
2169                        accountTotal = accountTotal.add(encumbranceAmount);
2170
2171                        lastAccount = account;
2172
2173                        if (LOG.isDebugEnabled()) {
2174                            LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " " + acctString + " = " + encumbranceAmount);
2175                        }
2176
2177                        if (encumbranceAccountMap.get(acctString) == null) {
2178                            encumbranceAccountMap.put(acctString, encumbranceAmount);
2179                        } else {
2180                            KualiDecimal amt = (KualiDecimal) encumbranceAccountMap.get(acctString);
2181                            encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount));
2182                        }
2183                    }
2184                }
2185
2186                // account for rounding by adjusting last account as needed
2187                if (lastAccount != null) {
2188                    KualiDecimal difference = itemDisEncumber.subtract(accountTotal);
2189                    if (LOG.isDebugEnabled()) {
2190                        LOG.debug("getCreditMemoEncumbrance() difference: " + logItmNbr + " " + difference);
2191                    }
2192
2193                    SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
2194                    KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
2195                    if (amount == null) {
2196                        encumbranceAccountMap.put(acctString, difference);
2197                    } else {
2198                        encumbranceAccountMap.put(acctString, amount.add(difference));
2199                    }
2200                    lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference));
2201                }
2202            }
2203        }
2204
2205        List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
2206        for (Iterator iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
2207            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
2208            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
2209            if (amount.doubleValue() != 0) {
2210                acctString.setAmount(amount);
2211                encumbranceAccounts.add(acctString);
2212            }
2213        }
2214
2215        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
2216        //MSU Contribution OLEMI-8639 DTT-4009 OLECNTRB-966
2217        // SpringContext.getBean(BusinessObjectService.class).save(po);
2218
2219        return encumbranceAccounts;
2220    }
2221
2222    /**
2223     * Save the given general ledger entries
2224     *
2225     * @param glEntries List of GeneralLedgerPendingEntries to be saved
2226     */
2227    protected void saveGLEntries(List<GeneralLedgerPendingEntry> glEntries) {
2228        LOG.debug("saveGLEntries() started");
2229        businessObjectService.save(glEntries);
2230    }
2231
2232    /**
2233     * Save the given accounts for the given document.
2234     *
2235     * @param summaryAccounts         Accounts to be saved
2236     * @param purapDocumentIdentifier Purap document id for accounts
2237     */
2238    protected void saveAccountsPayableSummaryAccounts(List<SummaryAccount> summaryAccounts, Integer purapDocumentIdentifier, String docType) {
2239        LOG.debug("saveAccountsPayableSummaryAccounts() started");
2240        purapAccountingService.deleteSummaryAccounts(purapDocumentIdentifier, docType);
2241        List<AccountsPayableSummaryAccount> apSummaryAccounts = new ArrayList();
2242        for (SummaryAccount summaryAccount : summaryAccounts) {
2243            apSummaryAccounts.add(new AccountsPayableSummaryAccount(summaryAccount.getAccount(), purapDocumentIdentifier, docType));
2244        }
2245        businessObjectService.save(apSummaryAccounts);
2246    }
2247
2248    /**
2249     * Save the given accounts for the given document.
2250     *
2251     * @param summaryAccounts         Accounts to be saved
2252     * @param purapDocumentIdentifier Purap document id for accounts
2253     * @param docType                 Document Type Used
2254     */
2255    protected void saveInvoiceAccountsPayableSummaryAccounts(List<SummaryAccount> summaryAccounts, Integer purapDocumentIdentifier, String docType) {
2256        LOG.debug("saveAccountsPayableSummaryAccounts() started");
2257        purapAccountingService.deleteSummaryAccounts(purapDocumentIdentifier, docType);
2258        List<OleInvoiceAccountsPayableSummaryAccount> apSummaryAccounts = new ArrayList();
2259        for (SummaryAccount summaryAccount : summaryAccounts) {
2260            apSummaryAccounts.add(new OleInvoiceAccountsPayableSummaryAccount(summaryAccount.getAccount(), purapDocumentIdentifier, docType));
2261        }
2262        businessObjectService.save(apSummaryAccounts);
2263    }
2264
2265    /**
2266     * Find item in PO based on given parameters. Must send either the line # or item type.
2267     *
2268     * @param po       Purchase Order containing list of items
2269     * @param nbr      Line # of desired item (could be null)
2270     * @param itemType Item type of desired item
2271     * @return PurcahseOrderItem found matching given criteria
2272     */
2273    protected PurchaseOrderItem getPoItem(PurchaseOrderDocument po, Integer nbr, ItemType itemType) {
2274        LOG.debug("getPoItem() started");
2275        for (Iterator iter = po.getItems().iterator(); iter.hasNext(); ) {
2276            PurchaseOrderItem element = (PurchaseOrderItem) iter.next();
2277            if (itemType.isLineItemIndicator()) {
2278                if (ObjectUtils.isNotNull(nbr) && ObjectUtils.isNotNull(element.getItemLineNumber()) && (nbr.compareTo(element.getItemLineNumber()) == 0)) {
2279                    return element;
2280                }
2281            } else {
2282                if (element.getItemTypeCode().equals(itemType.getItemTypeCode())) {
2283                    return element;
2284                }
2285            }
2286        }
2287        return null;
2288    }
2289
2290    /**
2291     * Format description for general ledger entry. Currently making sure length is less than 40 char.
2292     *
2293     * @param description String to be formatted
2294     * @return Formatted String
2295     */
2296    protected String entryDescription(String description) {
2297        if (description != null && description.length() > 40) {
2298            return description.toString().substring(0, 39);
2299        } else {
2300            return description;
2301        }
2302    }
2303
2304    /**
2305     * Calculate quantity change for creating Credit Memo entries
2306     *
2307     * @param cancel     Boolean indicating whether entries are for creation or cancellation of credit memo
2308     * @param poItem     Purchase Order Item
2309     * @param cmQuantity Quantity on credit memo item
2310     * @return Calculated change
2311     */
2312    protected KualiDecimal calculateQuantityChange(boolean cancel, PurchaseOrderItem poItem, KualiDecimal cmQuantity) {
2313        LOG.debug("calculateQuantityChange() started");
2314
2315        // Calculate quantity change & adjust invoiced quantity & outstanding encumbered quantity
2316        KualiDecimal encumbranceQuantityChange = null;
2317        if (cancel) {
2318            encumbranceQuantityChange = cmQuantity.multiply(new KualiDecimal("-1"));
2319        } else {
2320            encumbranceQuantityChange = cmQuantity;
2321        }
2322        poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().subtract(encumbranceQuantityChange));
2323        poItem.setItemOutstandingEncumberedQuantity(poItem.getItemOutstandingEncumberedQuantity().add(encumbranceQuantityChange));
2324
2325        // Check for overflows
2326        if (cancel) {
2327            if (poItem.getItemOutstandingEncumberedQuantity().doubleValue() < 0) {
2328                LOG.debug("calculateQuantityChange() Cancel overflow");
2329                KualiDecimal difference = poItem.getItemOutstandingEncumberedQuantity().abs();
2330                poItem.setItemOutstandingEncumberedQuantity(ZERO);
2331                poItem.setItemInvoicedTotalQuantity(poItem.getItemQuantity());
2332                encumbranceQuantityChange = encumbranceQuantityChange.add(difference);
2333            }
2334        }
2335// Coomented for JIRA:OLE-5162 as encumbrance get doubled here.
2336       /* else {
2337            if (poItem.getItemInvoicedTotalQuantity().doubleValue() < 0) {
2338                LOG.debug("calculateQuantityChange() Create overflow");
2339                KualiDecimal difference = poItem.getItemInvoicedTotalQuantity().abs();
2340                poItem.setItemOutstandingEncumberedQuantity(poItem.getItemQuantity());
2341                poItem.setItemInvoicedTotalQuantity(ZERO);
2342                encumbranceQuantityChange = encumbranceQuantityChange.add(difference);
2343            }
2344        } */
2345        return encumbranceQuantityChange;
2346    }
2347
2348    public void setDateTimeService(DateTimeService dateTimeService) {
2349        this.dateTimeService = dateTimeService;
2350    }
2351
2352    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
2353        this.businessObjectService = businessObjectService;
2354    }
2355
2356    public void setGeneralLedgerPendingEntryService(GeneralLedgerPendingEntryService generalLedgerPendingEntryService) {
2357        this.generalLedgerPendingEntryService = generalLedgerPendingEntryService;
2358    }
2359
2360    public void setKualiRuleService(KualiRuleService kualiRuleService) {
2361        this.kualiRuleService = kualiRuleService;
2362    }
2363
2364    public void setPurapAccountingService(PurapAccountingService purapAccountingService) {
2365        this.purapAccountingService = purapAccountingService;
2366    }
2367
2368    public void setUniversityDateService(UniversityDateService universityDateService) {
2369        this.universityDateService = universityDateService;
2370    }
2371
2372    public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
2373        this.purchaseOrderService = purchaseOrderService;
2374    }
2375
2376    public void setObjectCodeService(ObjectCodeService objectCodeService) {
2377        this.objectCodeService = objectCodeService;
2378    }
2379
2380    public void setSubObjectCodeService(SubObjectCodeService subObjectCodeService) {
2381        this.subObjectCodeService = subObjectCodeService;
2382    }
2383
2384    public void setParameterService(ParameterService parameterService) {
2385        this.parameterService = parameterService;
2386    }
2387
2388    public void setPaymentRequestService(PaymentRequestService paymentRequestService) {
2389        this.paymentRequestService = paymentRequestService;
2390    }
2391
2392    /**
2393     * Sets the invoiceService attribute value.
2394     *
2395     * @param invoiceService The invoiceService to set.
2396     */
2397
2398    public void setInvoiceService(InvoiceService invoiceService) {
2399        this.invoiceService = invoiceService;
2400    }
2401
2402
2403}