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"), 5, BigDecimal.ROUND_HALF_UP);
803                    BigDecimal amount = item.getItemOutstandingEncumberedAmount().bigDecimalValue().multiply(percent);
804                    account.setItemAccountOutstandingEncumbranceAmount(new KualiDecimal(amount));
805                            account.setAlternateAmountForGLEntryCreation(account.getItemAccountOutstandingEncumbranceAmount());
806                }
807            }
808        }
809
810        PurchaseOrderDocument oldPO = purchaseOrderService.getCurrentPurchaseOrder(po.getPurapDocumentIdentifier());
811
812        if (oldPO == null) {
813            throw new IllegalArgumentException("Current Purchase Order not found - poId = " + oldPO.getPurapDocumentIdentifier());
814        }
815
816        List newAccounts = purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly());
817        List oldAccounts = purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(oldPO.getItemsActiveOnlySetupAlternateAmount());
818
819        Map combination = new HashMap();
820
821        // Add amounts from the new PO
822        for (Iterator iter = newAccounts.iterator(); iter.hasNext(); ) {
823            SourceAccountingLine newAccount = (SourceAccountingLine) iter.next();
824            combination.put(newAccount, newAccount.getAmount());
825        }
826
827        LOG.debug("generateEntriesApproveAmendPurchaseOrder() combination after the add");
828        for (Iterator iter = combination.keySet().iterator(); iter.hasNext(); ) {
829            SourceAccountingLine element = (SourceAccountingLine) iter.next();
830            if (LOG.isDebugEnabled())
831                LOG.debug("generateEntriesApproveAmendPurchaseOrder() " + element + " = " + ((KualiDecimal) combination.get(element)).floatValue());
832        }
833
834        // Subtract the amounts from the old PO
835        for (Iterator iter = oldAccounts.iterator(); iter.hasNext(); ) {
836            SourceAccountingLine oldAccount = (SourceAccountingLine) iter.next();
837            if (combination.containsKey(oldAccount)) {
838                KualiDecimal amount = (KualiDecimal) combination.get(oldAccount);
839                amount = amount.subtract(oldAccount.getAmount());
840                combination.put(oldAccount, amount);
841            } else {
842                combination.put(oldAccount, ZERO.subtract(oldAccount.getAmount()));
843            }
844        }
845
846        LOG.debug("generateEntriesApproveAmendPurchaseOrder() combination after the subtract");
847        for (Iterator iter = combination.keySet().iterator(); iter.hasNext(); ) {
848            SourceAccountingLine element = (SourceAccountingLine) iter.next();
849            if (LOG.isDebugEnabled())
850                LOG.debug("generateEntriesApproveAmendPurchaseOrder() " + element + " = " + ((KualiDecimal) combination.get(element)).floatValue());
851        }
852
853        List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
854        for (Iterator iter = combination.keySet().iterator(); iter.hasNext(); ) {
855            SourceAccountingLine account = (SourceAccountingLine) iter.next();
856            KualiDecimal amount = (KualiDecimal) combination.get(account);
857            if (ZERO.compareTo(amount) != 0) {
858                account.setAmount(amount);
859                encumbranceAccounts.add(account);
860            }
861        }
862
863        po.setGlOnlySourceAccountingLines(encumbranceAccounts);
864        generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
865        saveGLEntries(po.getGeneralLedgerPendingEntries());
866        LOG.debug("generateEntriesApproveAmendPo() gl entries created; exit method");
867    }
868
869    /**
870     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesClosePurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
871     */
872    public void generateEntriesClosePurchaseOrder(PurchaseOrderDocument po) {
873        LOG.debug("generateEntriesClosePurchaseOrder() started");
874
875        // Set outstanding encumbered quantity/amount on items
876        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
877            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
878
879            String logItmNbr = "Item # " + item.getItemLineNumber();
880
881            if (!item.isItemActiveIndicator()) {
882                continue;
883            }
884
885            KualiDecimal itemAmount = null;
886            if (LOG.isDebugEnabled()) {
887                LOG.debug("generateEntriesClosePurchaseOrder() " + logItmNbr + " Calculate based on amounts");
888            }
889            itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
890
891            KualiDecimal accountTotal = ZERO;
892            PurchaseOrderAccount lastAccount = null;
893            if (itemAmount.compareTo(ZERO) != 0) {
894                // Sort accounts
895                Collections.sort((List) item.getSourceAccountingLines());
896
897                for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext(); ) {
898                    PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
899                    if (!acct.isEmpty()) {
900                        KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
901                        accountTotal = accountTotal.add(acctAmount);
902                        acct.setAlternateAmountForGLEntryCreation(acctAmount);
903                        lastAccount = acct;
904                    }
905                }
906
907                // account for rounding by adjusting last account as needed
908                if (lastAccount != null) {
909                    KualiDecimal difference = itemAmount.subtract(accountTotal);
910                    if (LOG.isDebugEnabled()) {
911                        LOG.debug("generateEntriesClosePurchaseOrder() difference: " + logItmNbr + " " + difference);
912                    }
913
914                    KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
915                    if (ObjectUtils.isNotNull(amount)) {
916                        lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
917                    } else {
918                        lastAccount.setAlternateAmountForGLEntryCreation(difference);
919                    }
920                }
921
922            }
923        }// endfor
924
925        po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
926        if (shouldGenerateGLPEForPurchaseOrder(po)) {
927            generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
928            saveGLEntries(po.getGeneralLedgerPendingEntries());
929            LOG.debug("generateEntriesClosePurchaseOrder() gl entries created; exit method");
930        }
931
932        //MSU Contribution DTT-3812 OLEMI-8642 OLECNTRB-957
933        // Set outstanding encumbered quantity/amount on items
934        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
935            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
936            if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
937                item.setItemOutstandingEncumberedQuantity(KualiDecimal.ZERO);
938            }
939            item.setItemOutstandingEncumberedAmount(KualiDecimal.ZERO);
940            List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
941            for (PurApAccountingLine purApAccountingLine : sourceAccountingLines) {
942                PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine;
943                account.setItemAccountOutstandingEncumbranceAmount(KualiDecimal.ZERO);
944            }
945
946        }// endfor
947
948        LOG.debug("generateEntriesClosePurchaseOrder() no gl entries created because the amount is 0; exit method");
949    }
950
951    /**
952     * We should not generate general ledger pending entries for Purchase Order Close Document and
953     * Purchase Order Reopen Document with $0 amount.
954     *
955     * @param po
956     * @return
957     */
958    protected boolean shouldGenerateGLPEForPurchaseOrder(PurchaseOrderDocument po) {
959        for (SourceAccountingLine acct : (List<SourceAccountingLine>) po.getSourceAccountingLines()) {
960            if (acct.getAmount().abs().compareTo(new KualiDecimal(0)) > 0) {
961                return true;
962            }
963        }
964        return false;
965    }
966
967    /**
968     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesReopenPurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
969     */
970    public void generateEntriesReopenPurchaseOrder(PurchaseOrderDocument po) {
971        LOG.debug("generateEntriesReopenPurchaseOrder() started");
972
973        //MSU Contribution DTT-3812 OLEMI-8642 OLECNTRB-957
974        // Set outstanding encumbered quantity/amount on items
975        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
976            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
977
978            // if invoice fields are null (as would be for new items), set fields to zero
979            item.setItemInvoicedTotalAmount(item.getItemInvoicedTotalAmount() == null ? ZERO : item.getItemInvoicedTotalAmount());
980            item.setItemInvoicedTotalQuantity(item.getItemInvoicedTotalQuantity() == null ? ZERO : item.getItemInvoicedTotalQuantity());
981
982            if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
983                item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity());
984                item.setItemOutstandingEncumberedQuantity(item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity()));
985                if(item.getItemOutstandingEncumberedQuantity().isGreaterThan(new KualiDecimal(0))) {
986                    item.setItemOutstandingEncumberedAmount(new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue().multiply(item.getItemUnitPrice())));
987                }
988                else {
989                    item.setItemOutstandingEncumberedAmount(new KualiDecimal(item.getItemQuantity().bigDecimalValue().multiply(item.getItemUnitPrice())));
990                    item.setItemOutstandingEncumberedQuantity(new KualiDecimal(item.getItemQuantity().bigDecimalValue()));
991                }
992            } else {
993                if((item.getTotalAmount().subtract(item.getItemInvoicedTotalAmount()).isGreaterThan(new KualiDecimal(0)))) {
994                    item.setItemOutstandingEncumberedAmount(item.getTotalAmount().subtract(item.getItemInvoicedTotalAmount()));
995                }
996                else {
997                    item.setItemOutstandingEncumberedAmount(item.getTotalAmount());
998                    item.setItemOutstandingEncumberedQuantity(new KualiDecimal(item.getItemQuantity().bigDecimalValue()));
999                }
1000            }
1001            List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
1002            for (PurApAccountingLine purApAccountingLine : sourceAccountingLines) {
1003                PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine;
1004                account.setItemAccountOutstandingEncumbranceAmount(new KualiDecimal(item.getItemOutstandingEncumberedAmount().bigDecimalValue().multiply(account.getAccountLinePercent()).divide(OLEConstants.ONE_HUNDRED.bigDecimalValue())));
1005            }
1006        }// endfor
1007
1008        // Set outstanding encumbered quantity/amount on items
1009        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
1010            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
1011
1012            String logItmNbr = "Item # " + item.getItemLineNumber();
1013
1014            if (!item.isItemActiveIndicator()) {
1015                continue;
1016            }
1017
1018            KualiDecimal itemAmount = null;
1019            if (item.getItemType().isAmountBasedGeneralLedgerIndicator()) {
1020                if (LOG.isDebugEnabled()) {
1021                    LOG.debug("generateEntriesReopenPurchaseOrder() " + logItmNbr + " Calculate based on amounts");
1022                }
1023                itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
1024            } else {
1025                if (LOG.isDebugEnabled()) {
1026                    LOG.debug("generateEntriesReopenPurchaseOrder() " + logItmNbr + " Calculate based on quantities");
1027                }
1028                //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
1029                itemAmount = new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue().multiply(item.getItemUnitPrice()));
1030            }
1031
1032            KualiDecimal accountTotal = ZERO;
1033            PurchaseOrderAccount lastAccount = null;
1034            if (itemAmount.compareTo(ZERO) != 0) {
1035                // Sort accounts
1036                Collections.sort((List) item.getSourceAccountingLines());
1037
1038                for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext(); ) {
1039                    PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
1040                    if (!acct.isEmpty()) {
1041                        KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
1042                        accountTotal = accountTotal.add(acctAmount);
1043                        acct.setAlternateAmountForGLEntryCreation(acctAmount);
1044                        lastAccount = acct;
1045                    }
1046                }
1047
1048                // account for rounding by adjusting last account as needed
1049                if (lastAccount != null) {
1050                    KualiDecimal difference = itemAmount.subtract(accountTotal);
1051                    if (LOG.isDebugEnabled()) {
1052                        LOG.debug("generateEntriesReopenPurchaseOrder() difference: " + logItmNbr + " " + difference);
1053                    }
1054
1055                    KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
1056                    if (ObjectUtils.isNotNull(amount)) {
1057                        lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
1058                    } else {
1059                        lastAccount.setAlternateAmountForGLEntryCreation(difference);
1060                    }
1061                }
1062
1063            }
1064        }// endfor
1065
1066        po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
1067        if (shouldGenerateGLPEForPurchaseOrder(po)) {
1068            generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
1069            saveGLEntries(po.getGeneralLedgerPendingEntries());
1070            LOG.debug("generateEntriesReopenPurchaseOrder() gl entries created; exit method");
1071        }
1072        LOG.debug("generateEntriesReopenPurchaseOrder() no gl entries created because the amount is 0; exit method");
1073    }
1074
1075    /**
1076     * @see org.kuali.ole.module.purap.service.PurapGeneralLedgerService#generateEntriesVoidPurchaseOrder(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
1077     */
1078    public void generateEntriesVoidPurchaseOrder(PurchaseOrderDocument po) {
1079        LOG.debug("generateEntriesVoidPurchaseOrder() started");
1080
1081        // Set outstanding encumbered quantity/amount on items
1082        for (Iterator items = po.getItems().iterator(); items.hasNext(); ) {
1083            PurchaseOrderItem item = (PurchaseOrderItem) items.next();
1084
1085            String logItmNbr = "Item # " + item.getItemLineNumber();
1086
1087            if (!item.isItemActiveIndicator()) {
1088                continue;
1089            }
1090
1091            //just use the outstanding amount as recalculating here, particularly the item tax will cause
1092            //amounts to be over or under encumbered and the remaining encumbered amount should be unencumbered during a close
1093            if (LOG.isDebugEnabled()) {
1094                LOG.debug("generateEntriesVoidPurchaseOrder() " + logItmNbr + " Calculate based on amounts");
1095            }
1096            KualiDecimal itemAmount = item.getItemOutstandingEncumberedAmount() == null ? ZERO : item.getItemOutstandingEncumberedAmount();
1097
1098            KualiDecimal accountTotal = ZERO;
1099            PurchaseOrderAccount lastAccount = null;
1100            if (itemAmount.compareTo(ZERO) != 0) {
1101                // Sort accounts
1102                Collections.sort((List) item.getSourceAccountingLines());
1103
1104                for (Iterator iterAcct = item.getSourceAccountingLines().iterator(); iterAcct.hasNext(); ) {
1105                    PurchaseOrderAccount acct = (PurchaseOrderAccount) iterAcct.next();
1106                    if (!acct.isEmpty()) {
1107                        KualiDecimal acctAmount = itemAmount.multiply(new KualiDecimal(acct.getAccountLinePercent().toString())).divide(PurapConstants.HUNDRED);
1108                        accountTotal = accountTotal.add(acctAmount);
1109                        acct.setAlternateAmountForGLEntryCreation(acctAmount);
1110                        lastAccount = acct;
1111                    }
1112                }
1113
1114                // account for rounding by adjusting last account as needed
1115                if (lastAccount != null) {
1116                    KualiDecimal difference = itemAmount.subtract(accountTotal);
1117                    if (LOG.isDebugEnabled()) {
1118                        LOG.debug("generateEntriesVoidPurchaseOrder() difference: " + logItmNbr + " " + difference);
1119                    }
1120
1121                    KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation();
1122                    if (ObjectUtils.isNotNull(amount)) {
1123                        lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference));
1124                    } else {
1125                        lastAccount.setAlternateAmountForGLEntryCreation(difference);
1126                    }
1127                }
1128
1129            }
1130        }// endfor
1131
1132        po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()));
1133        generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po);
1134        saveGLEntries(po.getGeneralLedgerPendingEntries());
1135        LOG.debug("generateEntriesVoidPurchaseOrder() gl entries created; exit method");
1136    }
1137
1138    /**
1139     * 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
1140     * the encumbrance values on the PO and saves the PO
1141     *
1142     * @param preq PREQ for invoice
1143     * @return List of accounting lines to use to create the pending general ledger entries
1144     */
1145    protected List<SourceAccountingLine> relieveEncumbrance(PaymentRequestDocument preq) {
1146        LOG.debug("relieveEncumbrance() started");
1147
1148        Map encumbranceAccountMap = new HashMap();
1149        PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(preq.getPurchaseOrderIdentifier());
1150
1151        // Get each item one by one
1152        for (Iterator items = preq.getItems().iterator(); items.hasNext(); ) {
1153            PaymentRequestItem preqItem = (PaymentRequestItem) items.next();
1154            PurchaseOrderItem poItem = getPoItem(po, preqItem.getItemLineNumber(), preqItem.getItemType());
1155
1156            boolean takeAll = false; // Set this true if we relieve the entire encumbrance
1157            KualiDecimal itemDisEncumber = null; // Amount to disencumber for this item
1158
1159            String logItmNbr = "Item # " + preqItem.getItemLineNumber();
1160            if (LOG.isDebugEnabled()) {
1161                LOG.debug("relieveEncumbrance() " + logItmNbr);
1162            }
1163
1164            // If there isn't a PO item or the extended price is 0, we don't need encumbrances
1165            if (poItem == null) {
1166                if (LOG.isDebugEnabled()) {
1167                    LOG.debug("relieveEncumbrance() " + logItmNbr + " No encumbrances required because po item is null");
1168                }
1169            } else {
1170                final KualiDecimal preqItemTotalAmount = (preqItem.getTotalAmount() == null) ? KualiDecimal.ZERO : preqItem.getTotalAmount();
1171                if (ZERO.compareTo(preqItemTotalAmount) == 0) {
1172                    /*
1173                     * This is a specialized case where PREQ item being processed must adjust the PO item's outstanding encumbered
1174                     * quantity. This kind of scenario is mostly seen on warranty type items. The following must be true to do this:
1175                     * PREQ item Extended Price must be ZERO, PREQ item invoice quantity must be not empty and not ZERO, and PO item
1176                     * is quantity based PO item unit cost is ZERO
1177                     */
1178                    if (LOG.isDebugEnabled()) {
1179                        LOG.debug("relieveEncumbrance() " + logItmNbr + " No GL encumbrances required because extended price is ZERO");
1180                    }
1181                    if ((poItem.getItemQuantity() != null) && ((BigDecimal.ZERO.compareTo(poItem.getItemUnitPrice())) == 0)) {
1182                        // po has order quantity and unit price is ZERO... reduce outstanding encumbered quantity
1183                        if (LOG.isDebugEnabled()) {
1184                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate po oustanding encumbrance");
1185                        }
1186
1187                        // Do encumbrance calculations based on quantity
1188                        if ((preqItem.getItemQuantity() != null) && ((ZERO.compareTo(preqItem.getItemQuantity())) != 0)) {
1189                            KualiDecimal invoiceQuantity = preqItem.getItemQuantity();
1190                            KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1191
1192                            KualiDecimal encumbranceQuantity;
1193                            if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
1194                                // We bought more than the quantity on the PO
1195                                if (LOG.isDebugEnabled()) {
1196                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1197                                }
1198                                encumbranceQuantity = outstandingEncumberedQuantity;
1199                                poItem.setItemOutstandingEncumberedQuantity(ZERO);
1200                            } else {
1201                                encumbranceQuantity = invoiceQuantity;
1202                                poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
1203                                if (LOG.isDebugEnabled()) {
1204                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " adjusting oustanding encunbrance qty - encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
1205                                }
1206                            }
1207
1208                            if (poItem.getItemInvoicedTotalQuantity() == null) {
1209                                poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
1210                            } else {
1211                                poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
1212                            }
1213                        }
1214                    }
1215
1216
1217                } else {
1218                    if (LOG.isDebugEnabled()) {
1219                        LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
1220                    }
1221
1222                    // Do we calculate the encumbrance amount based on quantity or amount?
1223                    if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1224                        if (LOG.isDebugEnabled()) {
1225                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
1226                        }
1227
1228                        // Do encumbrance calculations based on quantity
1229                        KualiDecimal invoiceQuantity = preqItem.getItemQuantity() == null ? ZERO : preqItem.getItemQuantity();
1230                        KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1231
1232                        KualiDecimal encumbranceQuantity;
1233
1234                        if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
1235                            // We bought more than the quantity on the PO
1236                            if (LOG.isDebugEnabled()) {
1237                                LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1238                            }
1239                            encumbranceQuantity = outstandingEncumberedQuantity;
1240                            poItem.setItemOutstandingEncumberedQuantity(ZERO);
1241                            takeAll = true;
1242                        }
1243                        else if (outstandingEncumberedQuantity.isLessEqual(new KualiDecimal(0))) {
1244                            // We bought more than the quantity on the PO
1245                            //  if (LOG.isDebugEnabled()) {
1246                            LOG.info("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1247                            // }
1248                            encumbranceQuantity = poItem.getItemQuantity();
1249                            poItem.setItemOutstandingEncumberedQuantity(ZERO);
1250                            takeAll = true;
1251                        }
1252
1253                        else {
1254                            encumbranceQuantity = invoiceQuantity;
1255                            poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
1256                            if (ZERO.compareTo(poItem.getItemOutstandingEncumberedQuantity()) == 0) {
1257                                takeAll = true;
1258                            }
1259                            if (LOG.isDebugEnabled()) {
1260                                LOG.debug("relieveEncumbrance() " + logItmNbr + " encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
1261                            }
1262                        }
1263
1264                        if (poItem.getItemInvoicedTotalQuantity() == null) {
1265                            poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
1266                        } else {
1267                            poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
1268                        }
1269
1270                        itemDisEncumber = new KualiDecimal(encumbranceQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
1271
1272                        //add tax for encumbrance
1273                        KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
1274                        KualiDecimal encumbranceTaxAmount = encumbranceQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
1275                        itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount);
1276                    } else {
1277                        if (LOG.isDebugEnabled()) {
1278                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
1279                        }
1280
1281                        // Do encumbrance calculations based on amount only
1282                        if ((poItem.getItemOutstandingEncumberedAmount().bigDecimalValue().signum() == -1) && (preqItemTotalAmount.bigDecimalValue().signum() == -1)) {
1283                            if (LOG.isDebugEnabled()) {
1284                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is negative: " + poItem.getItemOutstandingEncumberedAmount());
1285                            }
1286                            if (preqItemTotalAmount.compareTo(poItem.getItemOutstandingEncumberedAmount()) >= 0) {
1287                                // extended price is equal to or greater than outstanding encumbered
1288                                itemDisEncumber = preqItemTotalAmount;
1289                            }
1290                            else if (poItem.getItemOutstandingEncumberedAmount().isLessEqual(new KualiDecimal(0))) {
1291                                // We bought more than the quantity on the PO
1292                                //  if (LOG.isDebugEnabled()) {
1293                                LOG.info("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1294                                //  }
1295                                itemDisEncumber = poItem.getItemQuantity().multiply(new KualiDecimal(poItem.getItemUnitPrice()));
1296                                poItem.setItemOutstandingEncumberedQuantity(ZERO);
1297                                takeAll = true;
1298                            }
1299
1300                            else {
1301                                // extended price is less than outstanding encumbered
1302                                takeAll = true;
1303                                itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
1304                            }
1305                        } else {
1306                            if (LOG.isDebugEnabled()) {
1307                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is positive or ZERO: " + poItem.getItemOutstandingEncumberedAmount());
1308                            }
1309                            if (poItem.getItemOutstandingEncumberedAmount().compareTo(preqItemTotalAmount) >= 0) {
1310                                // outstanding amount is equal to or greater than extended price
1311                                itemDisEncumber = preqItemTotalAmount;
1312                            } else {
1313                                // outstanding amount is less than extended price
1314                                takeAll = true;
1315                                itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
1316                            }
1317                        }
1318                    }
1319
1320                    if (LOG.isDebugEnabled()) {
1321                        LOG.debug("relieveEncumbrance() " + logItmNbr + " Amount to disencumber: " + itemDisEncumber);
1322                    }
1323
1324                    KualiDecimal newOutstandingEncumberedAmount = new KualiDecimal(0);
1325                    if (poItem.getItemOutstandingEncumberedAmount().isLessEqual(new KualiDecimal(0))) {
1326                        newOutstandingEncumberedAmount = itemDisEncumber;
1327                    }
1328                    else {
1329                        newOutstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount().subtract(itemDisEncumber);
1330                    }
1331                    if (LOG.isDebugEnabled()) {
1332                        LOG.debug("relieveEncumbrance() " + logItmNbr + " New Outstanding Encumbered amount is : " + newOutstandingEncumberedAmount);
1333                    }
1334                    poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
1335
1336                    KualiDecimal newInvoicedTotalAmount = poItem.getItemInvoicedTotalAmount().add(preqItemTotalAmount);
1337                    if (LOG.isDebugEnabled()) {
1338                        LOG.debug("relieveEncumbrance() " + logItmNbr + " New Invoiced Total Amount is: " + newInvoicedTotalAmount);
1339                    }
1340                    poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
1341
1342                    // Sort accounts
1343                    Collections.sort((List) poItem.getSourceAccountingLines());
1344
1345                    // make the list of accounts for the disencumbrance entry
1346                    PurchaseOrderAccount lastAccount = null;
1347                    KualiDecimal accountTotal = ZERO;
1348                    for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
1349                        PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
1350                        if (!account.isEmpty()) {
1351                            KualiDecimal encumbranceAmount = null;
1352                            SourceAccountingLine acctString = account.generateSourceAccountingLine();
1353                            if (takeAll) {
1354                                // fully paid; remove remaining encumbrance
1355                                encumbranceAmount = account.getItemAccountOutstandingEncumbranceAmount();
1356                                account.setItemAccountOutstandingEncumbranceAmount(ZERO);
1357                                if (LOG.isDebugEnabled()) {
1358                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " take all");
1359                                }
1360                            } else {
1361                                // amount = item disencumber * account percent / 100
1362                                encumbranceAmount = itemDisEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
1363
1364                                account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().subtract(encumbranceAmount));
1365
1366                                // For rounding check at the end
1367                                accountTotal = accountTotal.add(encumbranceAmount);
1368
1369                                // If we are zeroing out the encumbrance, we don't need to adjust for rounding
1370                                if (!takeAll) {
1371                                    lastAccount = account;
1372                                }
1373                            }
1374
1375                            if (LOG.isDebugEnabled()) {
1376                                LOG.debug("relieveEncumbrance() " + logItmNbr + " " + acctString + " = " + encumbranceAmount);
1377                            }
1378                            if (ObjectUtils.isNull(encumbranceAccountMap.get(acctString))) {
1379                                encumbranceAccountMap.put(acctString, encumbranceAmount);
1380                            } else {
1381                                KualiDecimal amt = (KualiDecimal) encumbranceAccountMap.get(acctString);
1382                                encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount));
1383                            }
1384
1385                        }
1386                    }
1387
1388                    // account for rounding by adjusting last account as needed
1389                    if (lastAccount != null) {
1390                        KualiDecimal difference = itemDisEncumber.subtract(accountTotal);
1391                        if (LOG.isDebugEnabled()) {
1392                            LOG.debug("relieveEncumbrance() difference: " + logItmNbr + " " + difference);
1393                        }
1394
1395                        SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
1396                        KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1397                        if (ObjectUtils.isNull(amount)) {
1398                            encumbranceAccountMap.put(acctString, difference);
1399                        } else {
1400                            encumbranceAccountMap.put(acctString, amount.add(difference));
1401                        }
1402
1403                        lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().subtract(difference));
1404                    }
1405                }
1406            }
1407        }// endfor
1408
1409        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
1410
1411        List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
1412        for (Iterator iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
1413            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
1414            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1415            if (amount.doubleValue() != 0) {
1416                acctString.setAmount(amount);
1417                encumbranceAccounts.add(acctString);
1418            }
1419        }
1420        //SpringContext.getBean(BusinessObjectService.class).save(po);
1421        return encumbranceAccounts;
1422    }
1423
1424
1425    /**
1426     * 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
1427     * the encumbrance values on the PO and saves the PO
1428     *
1429     * @param inv inv for InvoiceDocument
1430     * @return List of accounting lines to use to create the pending general ledger entries
1431     */
1432    protected List<SourceAccountingLine> relieveEncumbrance(InvoiceDocument inv) {
1433        LOG.debug("relieveEncumbrance() started");
1434
1435        Map encumbranceAccountMap = new HashMap();
1436        // PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(inv.getPurchaseOrderIdentifier());
1437        for (PurchaseOrderDocument po : inv.getPurchaseOrderDocuments()) {
1438            // Get each item one by one
1439            for (Iterator items = inv.getItems().iterator(); items.hasNext(); ) {
1440                InvoiceItem invItem = (InvoiceItem) items.next();
1441                PurchaseOrderItem poItem = getPoItem(po, invItem.getItemLineNumber(), invItem.getItemType());
1442
1443                boolean takeAll = false; // Set this true if we relieve the entire encumbrance
1444                KualiDecimal itemDisEncumber = null; // Amount to disencumber for this item
1445
1446                String logItmNbr = "Item # " + invItem.getItemLineNumber();
1447                if (LOG.isDebugEnabled()) {
1448                    LOG.debug("relieveEncumbrance() " + logItmNbr);
1449                }
1450
1451                // If there isn't a PO item or the extended price is 0, we don't need encumbrances
1452                if (poItem == null) {
1453                    if (LOG.isDebugEnabled()) {
1454                        LOG.debug("relieveEncumbrance() " + logItmNbr + " No encumbrances required because po item is null");
1455                    }
1456                } else {
1457                    final KualiDecimal preqItemTotalAmount = (invItem.getTotalAmount() == null) ? KualiDecimal.ZERO : invItem.getTotalAmount();
1458                    if (ZERO.compareTo(preqItemTotalAmount) == 0) {
1459                    /*
1460                     * This is a specialized case where PREQ item being processed must adjust the PO item's outstanding encumbered
1461                     * quantity. This kind of scenario is mostly seen on warranty type items. The following must be true to do this:
1462                     * PREQ item Extended Price must be ZERO, PREQ item invoice quantity must be not empty and not ZERO, and PO item
1463                     * is quantity based PO item unit cost is ZERO
1464                     */
1465                        if (LOG.isDebugEnabled()) {
1466                            LOG.debug("relieveEncumbrance() " + logItmNbr + " No GL encumbrances required because extended price is ZERO");
1467                        }
1468                        if ((poItem.getItemQuantity() != null) && ((BigDecimal.ZERO.compareTo(poItem.getItemUnitPrice())) == 0)) {
1469                            // po has order quantity and unit price is ZERO... reduce outstanding encumbered quantity
1470                            if (LOG.isDebugEnabled()) {
1471                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate po oustanding encumbrance");
1472                            }
1473
1474                            // Do encumbrance calculations based on quantity
1475                            if ((invItem.getItemQuantity() != null) && ((ZERO.compareTo(invItem.getItemQuantity())) != 0)) {
1476                                KualiDecimal invoiceQuantity = invItem.getItemQuantity();
1477                                KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1478
1479                                KualiDecimal encumbranceQuantity;
1480                                if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
1481                                    // We bought more than the quantity on the PO
1482                                    if (LOG.isDebugEnabled()) {
1483                                        LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1484                                    }
1485                                    encumbranceQuantity = outstandingEncumberedQuantity;
1486                                    poItem.setItemOutstandingEncumberedQuantity(ZERO);
1487                                } else {
1488                                    encumbranceQuantity = invoiceQuantity;
1489                                    poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
1490                                    if (LOG.isDebugEnabled()) {
1491                                        LOG.debug("relieveEncumbrance() " + logItmNbr + " adjusting oustanding encunbrance qty - encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
1492                                    }
1493                                }
1494
1495                                if (poItem.getItemInvoicedTotalQuantity() == null) {
1496                                    poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
1497                                } else {
1498                                    poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
1499                                }
1500                            }
1501                        }
1502
1503
1504                    } else {
1505                        if (LOG.isDebugEnabled()) {
1506                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
1507                        }
1508
1509                        // Do we calculate the encumbrance amount based on quantity or amount?
1510                        if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1511                            if (LOG.isDebugEnabled()) {
1512                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
1513                            }
1514
1515                            // Do encumbrance calculations based on quantity
1516                            KualiDecimal invoiceQuantity = invItem.getItemQuantity() == null ? ZERO : invItem.getItemQuantity();
1517                            KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1518
1519                            KualiDecimal encumbranceQuantity;
1520
1521                            if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) {
1522                                // We bought more than the quantity on the PO
1523                                if (LOG.isDebugEnabled()) {
1524                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " we bought more than the qty on the PO");
1525                                }
1526                                encumbranceQuantity = outstandingEncumberedQuantity;
1527                                poItem.setItemOutstandingEncumberedQuantity(ZERO);
1528                                takeAll = true;
1529                            } else {
1530                                encumbranceQuantity = invoiceQuantity;
1531                                poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.subtract(encumbranceQuantity));
1532                                if (ZERO.compareTo(poItem.getItemOutstandingEncumberedQuantity()) == 0) {
1533                                    takeAll = true;
1534                                }
1535                                if (LOG.isDebugEnabled()) {
1536                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " encumbranceQty " + encumbranceQuantity + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity());
1537                                }
1538                            }
1539
1540                            if (poItem.getItemInvoicedTotalQuantity() == null) {
1541                                poItem.setItemInvoicedTotalQuantity(invoiceQuantity);
1542                            } else {
1543                                poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity));
1544                            }
1545
1546                            itemDisEncumber = new KualiDecimal(encumbranceQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
1547
1548                            //add tax for encumbrance
1549                            KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
1550                            KualiDecimal encumbranceTaxAmount = encumbranceQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
1551                            itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount);
1552                        } else {
1553                            if (LOG.isDebugEnabled()) {
1554                                LOG.debug("relieveEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
1555                            }
1556
1557                            // Do encumbrance calculations based on amount only
1558                            if ((poItem.getItemOutstandingEncumberedAmount().bigDecimalValue().signum() == -1) && (preqItemTotalAmount.bigDecimalValue().signum() == -1)) {
1559                                if (LOG.isDebugEnabled()) {
1560                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is negative: " + poItem.getItemOutstandingEncumberedAmount());
1561                                }
1562                                if (preqItemTotalAmount.compareTo(poItem.getItemOutstandingEncumberedAmount()) >= 0) {
1563                                    // extended price is equal to or greater than outstanding encumbered
1564                                    itemDisEncumber = preqItemTotalAmount;
1565                                } else {
1566                                    // extended price is less than outstanding encumbered
1567                                    takeAll = true;
1568                                    itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
1569                                }
1570                            } else {
1571                                if (LOG.isDebugEnabled()) {
1572                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " Outstanding Encumbered amount is positive or ZERO: " + poItem.getItemOutstandingEncumberedAmount());
1573                                }
1574                                if (poItem.getItemOutstandingEncumberedAmount().compareTo(preqItemTotalAmount) >= 0) {
1575                                    // outstanding amount is equal to or greater than extended price
1576                                    itemDisEncumber = preqItemTotalAmount;
1577                                } else {
1578                                    // outstanding amount is less than extended price
1579                                    takeAll = true;
1580                                    itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
1581                                }
1582                            }
1583                        }
1584
1585                        if (LOG.isDebugEnabled()) {
1586                            LOG.debug("relieveEncumbrance() " + logItmNbr + " Amount to disencumber: " + itemDisEncumber);
1587                        }
1588
1589                        KualiDecimal newOutstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount().subtract(itemDisEncumber);
1590                        if (LOG.isDebugEnabled()) {
1591                            LOG.debug("relieveEncumbrance() " + logItmNbr + " New Outstanding Encumbered amount is : " + newOutstandingEncumberedAmount);
1592                        }
1593                        poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
1594
1595                        KualiDecimal newInvoicedTotalAmount = poItem.getItemInvoicedTotalAmount().add(preqItemTotalAmount);
1596                        if (LOG.isDebugEnabled()) {
1597                            LOG.debug("relieveEncumbrance() " + logItmNbr + " New Invoiced Total Amount is: " + newInvoicedTotalAmount);
1598                        }
1599                        poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
1600
1601                        // Sort accounts
1602                        Collections.sort((List) poItem.getSourceAccountingLines());
1603
1604                        // make the list of accounts for the disencumbrance entry
1605                        PurchaseOrderAccount lastAccount = null;
1606                        KualiDecimal accountTotal = ZERO;
1607                        for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
1608                            PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
1609                            if (!account.isEmpty()) {
1610                                KualiDecimal encumbranceAmount = null;
1611                                SourceAccountingLine acctString = account.generateSourceAccountingLine();
1612                                if (takeAll) {
1613                                    // fully paid; remove remaining encumbrance
1614                                    encumbranceAmount = account.getItemAccountOutstandingEncumbranceAmount();
1615                                    account.setItemAccountOutstandingEncumbranceAmount(ZERO);
1616                                    if (LOG.isDebugEnabled()) {
1617                                        LOG.debug("relieveEncumbrance() " + logItmNbr + " take all");
1618                                    }
1619                                } else {
1620                                    // amount = item disencumber * account percent / 100
1621                                    encumbranceAmount = itemDisEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
1622
1623                                    account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().subtract(encumbranceAmount));
1624
1625                                    // For rounding check at the end
1626                                    accountTotal = accountTotal.add(encumbranceAmount);
1627
1628                                    // If we are zeroing out the encumbrance, we don't need to adjust for rounding
1629                                    if (!takeAll) {
1630                                        lastAccount = account;
1631                                    }
1632                                }
1633
1634                                if (LOG.isDebugEnabled()) {
1635                                    LOG.debug("relieveEncumbrance() " + logItmNbr + " " + acctString + " = " + encumbranceAmount);
1636                                }
1637                                if (ObjectUtils.isNull(encumbranceAccountMap.get(acctString))) {
1638                                    encumbranceAccountMap.put(acctString, encumbranceAmount);
1639                                } else {
1640                                    KualiDecimal amt = (KualiDecimal) encumbranceAccountMap.get(acctString);
1641                                    encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount));
1642                                }
1643
1644                            }
1645                        }
1646
1647                        // account for rounding by adjusting last account as needed
1648                        if (lastAccount != null) {
1649                            KualiDecimal difference = itemDisEncumber.subtract(accountTotal);
1650                            if (LOG.isDebugEnabled()) {
1651                                LOG.debug("relieveEncumbrance() difference: " + logItmNbr + " " + difference);
1652                            }
1653
1654                            SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
1655                            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1656                            if (ObjectUtils.isNull(amount)) {
1657                                encumbranceAccountMap.put(acctString, difference);
1658                            } else {
1659                                encumbranceAccountMap.put(acctString, amount.add(difference));
1660                            }
1661
1662                            lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().subtract(difference));
1663                        }
1664                    }
1665                }
1666            }// endfor
1667        }
1668
1669        List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
1670        for (Iterator iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
1671            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
1672            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1673            if (amount.doubleValue() != 0) {
1674                acctString.setAmount(amount);
1675                encumbranceAccounts.add(acctString);
1676            }
1677        }
1678
1679        // SpringContext.getBean(BusinessObjectService.class).save(po);
1680        return encumbranceAccounts;
1681    }
1682
1683
1684    /**
1685     * 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
1686     * encumbrance values on the PO and saves the PO
1687     *
1688     * @param preq PREQ for invoice
1689     * @return List of accounting lines to use to create the pending general ledger entries
1690     */
1691    protected List<SourceAccountingLine> reencumberEncumbrance(PaymentRequestDocument preq) {
1692        LOG.debug("reencumberEncumbrance() started");
1693
1694        PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(preq.getPurchaseOrderIdentifier());
1695        Map encumbranceAccountMap = new HashMap();
1696
1697        // Get each item one by one
1698        for (Iterator items = preq.getItems().iterator(); items.hasNext(); ) {
1699            PaymentRequestItem payRequestItem = (PaymentRequestItem) items.next();
1700            PurchaseOrderItem poItem = getPoItem(po, payRequestItem.getItemLineNumber(), payRequestItem.getItemType());
1701
1702            KualiDecimal itemReEncumber = null; // Amount to reencumber for this item
1703
1704            String logItmNbr = "Item # " + payRequestItem.getItemLineNumber();
1705            if (LOG.isDebugEnabled()) {
1706                LOG.debug("reencumberEncumbrance() " + logItmNbr);
1707            }
1708
1709            // If there isn't a PO item or the total amount is 0, we don't need encumbrances
1710            final KualiDecimal preqItemTotalAmount = (payRequestItem.getTotalAmount() == null) ? KualiDecimal.ZERO : payRequestItem.getTotalAmount();
1711            if ((poItem == null) || (preqItemTotalAmount.doubleValue() == 0)) {
1712                if (LOG.isDebugEnabled()) {
1713                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " No encumbrances required");
1714                }
1715            } else {
1716                if (LOG.isDebugEnabled()) {
1717                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
1718                }
1719
1720                // Do we calculate the encumbrance amount based on quantity or amount?
1721                if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1722                    if (LOG.isDebugEnabled()) {
1723                        LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
1724                    }
1725
1726                    // Do disencumbrance calculations based on quantity
1727                    KualiDecimal preqQuantity = payRequestItem.getItemQuantity() == null ? ZERO : payRequestItem.getItemQuantity();
1728                    KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1729                    KualiDecimal invoicedTotal = poItem.getItemInvoicedTotalQuantity() == null ? ZERO : poItem.getItemInvoicedTotalQuantity();
1730
1731                    poItem.setItemInvoicedTotalQuantity(invoicedTotal.subtract(preqQuantity));
1732                    poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.add(preqQuantity));
1733
1734                    //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
1735                    itemReEncumber = new KualiDecimal(preqQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
1736
1737                    //add tax for encumbrance
1738                    KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
1739                    KualiDecimal encumbranceTaxAmount = preqQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
1740                    itemReEncumber = itemReEncumber.add(encumbranceTaxAmount);
1741
1742                } else {
1743                    if (LOG.isDebugEnabled()) {
1744                        LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
1745                    }
1746
1747                    itemReEncumber = preqItemTotalAmount;
1748                    // if re-encumber amount is more than original PO ordered amount... do not exceed ordered amount
1749                    // this prevents negative encumbrance
1750                    if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() < 0)) {
1751                        // po item extended cost is negative
1752                        if ((poItem.getTotalAmount().compareTo(itemReEncumber)) > 0) {
1753                            itemReEncumber = poItem.getTotalAmount();
1754                        }
1755                    } else if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() >= 0)) {
1756                        // po item extended cost is positive
1757                        if ((poItem.getTotalAmount().compareTo(itemReEncumber)) < 0) {
1758                            itemReEncumber = poItem.getTotalAmount();
1759                        }
1760                    }
1761                }
1762
1763                if (LOG.isDebugEnabled()) {
1764                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " Amount to reencumber: " + itemReEncumber);
1765                }
1766
1767                KualiDecimal outstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount() == null ? ZERO : poItem.getItemOutstandingEncumberedAmount();
1768                if (LOG.isDebugEnabled()) {
1769                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Outstanding Encumbrance Amount set to: " + outstandingEncumberedAmount);
1770                }
1771                KualiDecimal newOutstandingEncumberedAmount = outstandingEncumberedAmount.add(itemReEncumber);
1772                if (LOG.isDebugEnabled()) {
1773                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Outstanding Encumbrance Amount to set: " + newOutstandingEncumberedAmount);
1774                }
1775                poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
1776
1777                KualiDecimal invoicedTotalAmount = poItem.getItemInvoicedTotalAmount() == null ? ZERO : poItem.getItemInvoicedTotalAmount();
1778                if (LOG.isDebugEnabled()) {
1779                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Invoiced Total Amount set to: " + invoicedTotalAmount);
1780                }
1781                KualiDecimal newInvoicedTotalAmount = invoicedTotalAmount.subtract(preqItemTotalAmount);
1782                if (LOG.isDebugEnabled()) {
1783                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Invoiced Total Amount to set: " + newInvoicedTotalAmount);
1784                }
1785                poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
1786
1787                // make the list of accounts for the reencumbrance entry
1788                PurchaseOrderAccount lastAccount = null;
1789                KualiDecimal accountTotal = ZERO;
1790
1791                // Sort accounts
1792                Collections.sort((List) poItem.getSourceAccountingLines());
1793
1794                for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
1795                    PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
1796                    if (!account.isEmpty()) {
1797                        SourceAccountingLine acctString = account.generateSourceAccountingLine();
1798
1799                        // amount = item reencumber * account percent / 100
1800                        KualiDecimal reencumbranceAmount = itemReEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
1801
1802                        account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().add(reencumbranceAmount));
1803
1804                        // For rounding check at the end
1805                        accountTotal = accountTotal.add(reencumbranceAmount);
1806
1807                        lastAccount = account;
1808
1809                        if (LOG.isDebugEnabled()) {
1810                            LOG.debug("reencumberEncumbrance() " + logItmNbr + " " + acctString + " = " + reencumbranceAmount);
1811                        }
1812                        if (encumbranceAccountMap.containsKey(acctString)) {
1813                            KualiDecimal currentAmount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1814                            encumbranceAccountMap.put(acctString, reencumbranceAmount.add(currentAmount));
1815                        } else {
1816                            encumbranceAccountMap.put(acctString, reencumbranceAmount);
1817                        }
1818                    }
1819                }
1820
1821                // account for rounding by adjusting last account as needed
1822                if (lastAccount != null) {
1823                    KualiDecimal difference = itemReEncumber.subtract(accountTotal);
1824                    if (LOG.isDebugEnabled()) {
1825                        LOG.debug("reencumberEncumbrance() difference: " + logItmNbr + " " + difference);
1826                    }
1827
1828                    SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
1829                    KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1830                    if (amount == null) {
1831                        encumbranceAccountMap.put(acctString, difference);
1832                    } else {
1833                        encumbranceAccountMap.put(acctString, amount.add(difference));
1834                    }
1835                    lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference));
1836                }
1837            }
1838        }
1839
1840        //SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
1841
1842        //MSU Contribution OLEMI-8639 DTT-4009 OLECNTRB-966
1843        //  SpringContext.getBean(BusinessObjectService.class).save(po);
1844
1845        List<SourceAccountingLine> encumbranceAccounts = new ArrayList<SourceAccountingLine>();
1846        for (Iterator<SourceAccountingLine> iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
1847            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
1848            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1849            if (amount.doubleValue() != 0) {
1850                acctString.setAmount(amount);
1851                encumbranceAccounts.add(acctString);
1852            }
1853        }
1854
1855        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
1856        return encumbranceAccounts;
1857    }
1858
1859
1860    protected List<SourceAccountingLine> reencumberEncumbrance(InvoiceDocument prqs) {
1861        LOG.debug("reencumberEncumbrance() started");
1862
1863        PurchaseOrderDocument po = null;
1864                //purchaseOrderService.getCurrentPurchaseOrder(prqs.getPurchaseOrderIdentifier());
1865        Map encumbranceAccountMap = new HashMap();
1866
1867        // Get each item one by one
1868        for (Iterator items = prqs.getItems().iterator(); items.hasNext(); ) {
1869            InvoiceItem invItem = (InvoiceItem) items.next();
1870            po = purchaseOrderService.getCurrentPurchaseOrder(invItem.getPurchaseOrderIdentifier());
1871            PurchaseOrderItem poItem = null;
1872            if(po != null) {
1873                 poItem = getPoItem(po, invItem.getItemLineNumber(), invItem.getItemType());
1874            }
1875
1876            KualiDecimal itemReEncumber = null; // Amount to reencumber for this item
1877
1878            String logItmNbr = "Item # " + invItem.getItemLineNumber();
1879            if (LOG.isDebugEnabled()) {
1880                LOG.debug("reencumberEncumbrance() " + logItmNbr);
1881            }
1882
1883            // If there isn't a PO item or the total amount is 0, we don't need encumbrances
1884            final KualiDecimal preqItemTotalAmount = (invItem.getTotalAmount() == null) ? KualiDecimal.ZERO : invItem.getTotalAmount();
1885            if ((poItem == null) || (preqItemTotalAmount.doubleValue() == 0)) {
1886                if (LOG.isDebugEnabled()) {
1887                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " No encumbrances required");
1888                }
1889            } else {
1890                if (LOG.isDebugEnabled()) {
1891                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
1892                }
1893
1894                // Do we calculate the encumbrance amount based on quantity or amount?
1895                if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
1896                    if (LOG.isDebugEnabled()) {
1897                        LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
1898                    }
1899
1900                    // Do disencumbrance calculations based on quantity
1901                    KualiDecimal preqQuantity = invItem.getItemQuantity() == null ? ZERO : invItem.getItemQuantity();
1902                    KualiDecimal outstandingEncumberedQuantity = poItem.getItemOutstandingEncumberedQuantity() == null ? ZERO : poItem.getItemOutstandingEncumberedQuantity();
1903                    KualiDecimal invoicedTotal = poItem.getItemInvoicedTotalQuantity() == null ? ZERO : poItem.getItemInvoicedTotalQuantity();
1904
1905                    poItem.setItemInvoicedTotalQuantity(invoicedTotal.subtract(preqQuantity));
1906                    poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.add(preqQuantity));
1907
1908                    //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
1909                    itemReEncumber = new KualiDecimal(preqQuantity.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
1910
1911                    //add tax for encumbrance
1912                    KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
1913                    KualiDecimal encumbranceTaxAmount = preqQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
1914                    itemReEncumber = itemReEncumber.add(encumbranceTaxAmount);
1915
1916                } else {
1917                    if (LOG.isDebugEnabled()) {
1918                        LOG.debug("reencumberEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
1919                    }
1920
1921                    itemReEncumber = preqItemTotalAmount;
1922                    // if re-encumber amount is more than original PO ordered amount... do not exceed ordered amount
1923                    // this prevents negative encumbrance
1924                    if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() < 0)) {
1925                        // po item extended cost is negative
1926                        if ((poItem.getTotalAmount().compareTo(itemReEncumber)) > 0) {
1927                            itemReEncumber = poItem.getTotalAmount();
1928                        }
1929                    } else if ((poItem.getTotalAmount() != null) && (poItem.getTotalAmount().bigDecimalValue().signum() >= 0)) {
1930                        // po item extended cost is positive
1931                        if ((poItem.getTotalAmount().compareTo(itemReEncumber)) < 0) {
1932                            itemReEncumber = poItem.getTotalAmount();
1933                        }
1934                    }
1935                }
1936
1937                if (LOG.isDebugEnabled()) {
1938                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " Amount to reencumber: " + itemReEncumber);
1939                }
1940
1941                KualiDecimal outstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount() == null ? ZERO : poItem.getItemOutstandingEncumberedAmount();
1942                if (LOG.isDebugEnabled()) {
1943                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Outstanding Encumbrance Amount set to: " + outstandingEncumberedAmount);
1944                }
1945                KualiDecimal newOutstandingEncumberedAmount = outstandingEncumberedAmount.add(itemReEncumber);
1946                if (LOG.isDebugEnabled()) {
1947                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Outstanding Encumbrance Amount to set: " + newOutstandingEncumberedAmount);
1948                }
1949                poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount);
1950
1951                KualiDecimal invoicedTotalAmount = poItem.getItemInvoicedTotalAmount() == null ? ZERO : poItem.getItemInvoicedTotalAmount();
1952                if (LOG.isDebugEnabled()) {
1953                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " PO Item Invoiced Total Amount set to: " + invoicedTotalAmount);
1954                }
1955                KualiDecimal newInvoicedTotalAmount = invoicedTotalAmount.subtract(preqItemTotalAmount);
1956                if (LOG.isDebugEnabled()) {
1957                    LOG.debug("reencumberEncumbrance() " + logItmNbr + " New PO Item Invoiced Total Amount to set: " + newInvoicedTotalAmount);
1958                }
1959                poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount);
1960
1961                // make the list of accounts for the reencumbrance entry
1962                PurchaseOrderAccount lastAccount = null;
1963                KualiDecimal accountTotal = ZERO;
1964
1965                // Sort accounts
1966                Collections.sort((List) poItem.getSourceAccountingLines());
1967
1968                for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
1969                    PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
1970                    if (!account.isEmpty()) {
1971                        SourceAccountingLine acctString = account.generateSourceAccountingLine();
1972
1973                        // amount = item reencumber * account percent / 100
1974                        KualiDecimal reencumbranceAmount = itemReEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(HUNDRED);
1975
1976                        account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().add(reencumbranceAmount));
1977
1978                        // For rounding check at the end
1979                        accountTotal = accountTotal.add(reencumbranceAmount);
1980
1981                        lastAccount = account;
1982
1983                        if (LOG.isDebugEnabled()) {
1984                            LOG.debug("reencumberEncumbrance() " + logItmNbr + " " + acctString + " = " + reencumbranceAmount);
1985                        }
1986                        if (encumbranceAccountMap.containsKey(acctString)) {
1987                            KualiDecimal currentAmount = (KualiDecimal) encumbranceAccountMap.get(acctString);
1988                            encumbranceAccountMap.put(acctString, reencumbranceAmount.add(currentAmount));
1989                        } else {
1990                            encumbranceAccountMap.put(acctString, reencumbranceAmount);
1991                        }
1992                    }
1993                }
1994
1995                // account for rounding by adjusting last account as needed
1996                if (lastAccount != null) {
1997                    KualiDecimal difference = itemReEncumber.subtract(accountTotal);
1998                    if (LOG.isDebugEnabled()) {
1999                        LOG.debug("reencumberEncumbrance() difference: " + logItmNbr + " " + difference);
2000                    }
2001
2002                    SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
2003                    KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
2004                    if (amount == null) {
2005                        encumbranceAccountMap.put(acctString, difference);
2006                    } else {
2007                        encumbranceAccountMap.put(acctString, amount.add(difference));
2008                    }
2009                    lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference));
2010                }
2011            }
2012        }
2013
2014        //SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
2015
2016        //MSU Contribution OLEMI-8639 DTT-4009 OLECNTRB-966
2017        // SpringContext.getBean(BusinessObjectService.class).save(po);
2018
2019        List<SourceAccountingLine> encumbranceAccounts = new ArrayList<SourceAccountingLine>();
2020        for (Iterator<SourceAccountingLine> iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
2021            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
2022            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
2023            if (amount.doubleValue() != 0) {
2024                acctString.setAmount(amount);
2025                encumbranceAccounts.add(acctString);
2026            }
2027        }
2028        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
2029        return encumbranceAccounts;
2030    }
2031
2032
2033    /**
2034     * 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
2035     * encumbrance values on the PO and saves the PO
2036     *
2037     * @param cm Credit Memo document
2038     * @param po Purchase Order document modify encumbrances
2039     * @return List of accounting lines to use to create the pending general ledger entries
2040     */
2041    protected List<SourceAccountingLine> getCreditMemoEncumbrance(VendorCreditMemoDocument cm, PurchaseOrderDocument po, boolean cancel) {
2042        LOG.debug("getCreditMemoEncumbrance() started");
2043
2044        if (ObjectUtils.isNull(po)) {
2045            return null;
2046        }
2047
2048        if (cancel) {
2049            LOG.debug("getCreditMemoEncumbrance() Receiving items back from vendor (cancelled CM)");
2050        } else {
2051            LOG.debug("getCreditMemoEncumbrance() Returning items to vendor");
2052        }
2053
2054        Map encumbranceAccountMap = new HashMap();
2055
2056        // Get each item one by one
2057        for (Iterator items = cm.getItems().iterator(); items.hasNext(); ) {
2058            CreditMemoItem cmItem = (CreditMemoItem) items.next();
2059            PurchaseOrderItem poItem = getPoItem(po, cmItem.getItemLineNumber(), cmItem.getItemType());
2060
2061            KualiDecimal itemDisEncumber = null; // Amount to disencumber for this item
2062            KualiDecimal itemAlterInvoiceAmt = null; // Amount to alter the invoicedAmt on the PO item
2063
2064            String logItmNbr = "Item # " + cmItem.getItemLineNumber();
2065            if (LOG.isDebugEnabled()) {
2066                LOG.debug("getCreditMemoEncumbrance() " + logItmNbr);
2067            }
2068
2069            final KualiDecimal cmItemTotalAmount = (cmItem.getTotalAmount() == null) ? KualiDecimal.ZERO : cmItem.getTotalAmount();
2070            ;
2071            // If there isn't a PO item or the total amount is 0, we don't need encumbrances
2072            if ((poItem == null) || (cmItemTotalAmount == null) || (cmItemTotalAmount.doubleValue() == 0) ||
2073                    (cmItemTotalAmount!=null && !cmItemTotalAmount.isNegative())) {
2074                if (LOG.isDebugEnabled()) {
2075                    LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " No encumbrances required");
2076                }
2077            } else {
2078                if (LOG.isDebugEnabled()) {
2079                    LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance GL entries");
2080                }
2081
2082                // Do we calculate the encumbrance amount based on quantity or amount?
2083                if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
2084                    if (LOG.isDebugEnabled()) {
2085                        LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance based on quantity");
2086                    }
2087
2088                    // Do encumbrance calculations based on quantity
2089                    KualiDecimal cmQuantity = cmItem.getItemQuantity() == null ? ZERO : cmItem.getItemQuantity();
2090
2091                    KualiDecimal encumbranceQuantityChange = calculateQuantityChange(cancel, poItem, cmQuantity);
2092
2093                    if (LOG.isDebugEnabled()) {
2094                        LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " encumbranceQtyChange " + encumbranceQuantityChange + " outstandingEncumberedQty " + poItem.getItemOutstandingEncumberedQuantity() + " invoicedTotalQuantity " + poItem.getItemInvoicedTotalQuantity());
2095                    }
2096
2097                    //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits
2098                    itemDisEncumber = new KualiDecimal(encumbranceQuantityChange.bigDecimalValue().multiply(poItem.getItemUnitPrice()));
2099
2100                    //add tax for encumbrance
2101                    KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? ZERO : poItem.getItemTaxAmount();
2102                    KualiDecimal encumbranceTaxAmount = encumbranceQuantityChange.divide(poItem.getItemQuantity()).multiply(itemTaxAmount);
2103                    itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount);
2104
2105                    itemAlterInvoiceAmt = cmItemTotalAmount;
2106                    if (cancel) {
2107                        itemAlterInvoiceAmt = itemAlterInvoiceAmt.multiply(new KualiDecimal("-1"));
2108                    }
2109                } else {
2110                    if (LOG.isDebugEnabled()) {
2111                        LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Calculate encumbrance based on amount");
2112                    }
2113
2114                    // Do encumbrance calculations based on amount only
2115                    if (cancel) {
2116                        // Decrease encumbrance
2117                        itemDisEncumber = cmItemTotalAmount.multiply(new KualiDecimal("-1"));
2118
2119                        if (poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber).doubleValue() < 0) {
2120                            LOG.debug("getCreditMemoEncumbrance() Cancel overflow");
2121
2122                            itemDisEncumber = poItem.getItemOutstandingEncumberedAmount();
2123                        }
2124                    } else {
2125                        // Increase encumbrance
2126                        itemDisEncumber = cmItemTotalAmount;
2127
2128                        if (poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber).doubleValue() > poItem.getTotalAmount().doubleValue()) {
2129                            LOG.debug("getCreditMemoEncumbrance() Create overflow");
2130
2131                            itemDisEncumber = poItem.getTotalAmount().subtract(poItem.getItemOutstandingEncumberedAmount());
2132                        }
2133                    }
2134                    itemAlterInvoiceAmt = itemDisEncumber;
2135                }
2136
2137                // alter the encumbrance based on what was originally encumbered
2138                poItem.setItemOutstandingEncumberedAmount(poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber));
2139
2140                // alter the invoiced amt based on what was actually credited on the credit memo
2141                poItem.setItemInvoicedTotalAmount(poItem.getItemInvoicedTotalAmount().subtract(itemAlterInvoiceAmt));
2142                if (poItem.getItemInvoicedTotalAmount().compareTo(ZERO) < 0) {
2143                    poItem.setItemInvoicedTotalAmount(ZERO);
2144                }
2145
2146
2147                if (LOG.isDebugEnabled()) {
2148                    LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " Amount to disencumber: " + itemDisEncumber);
2149                }
2150
2151                // Sort accounts
2152                Collections.sort((List) poItem.getSourceAccountingLines());
2153
2154                // make the list of accounts for the disencumbrance entry
2155                PurchaseOrderAccount lastAccount = null;
2156                KualiDecimal accountTotal = ZERO;
2157                // Collections.sort((List)poItem.getSourceAccountingLines());
2158                for (Iterator accountIter = poItem.getSourceAccountingLines().iterator(); accountIter.hasNext(); ) {
2159                    PurchaseOrderAccount account = (PurchaseOrderAccount) accountIter.next();
2160                    if (!account.isEmpty()) {
2161                        KualiDecimal encumbranceAmount = null;
2162
2163                        SourceAccountingLine acctString = account.generateSourceAccountingLine();
2164                        // amount = item disencumber * account percent / 100
2165                        encumbranceAmount = itemDisEncumber.multiply(new KualiDecimal(account.getAccountLinePercent().toString())).divide(new KualiDecimal(100));
2166
2167                        account.setItemAccountOutstandingEncumbranceAmount(account.getItemAccountOutstandingEncumbranceAmount().add(encumbranceAmount));
2168
2169                        // For rounding check at the end
2170                        accountTotal = accountTotal.add(encumbranceAmount);
2171
2172                        lastAccount = account;
2173
2174                        if (LOG.isDebugEnabled()) {
2175                            LOG.debug("getCreditMemoEncumbrance() " + logItmNbr + " " + acctString + " = " + encumbranceAmount);
2176                        }
2177
2178                        if (encumbranceAccountMap.get(acctString) == null) {
2179                            encumbranceAccountMap.put(acctString, encumbranceAmount);
2180                        } else {
2181                            KualiDecimal amt = (KualiDecimal) encumbranceAccountMap.get(acctString);
2182                            encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount));
2183                        }
2184                    }
2185                }
2186
2187                // account for rounding by adjusting last account as needed
2188                if (lastAccount != null) {
2189                    KualiDecimal difference = itemDisEncumber.subtract(accountTotal);
2190                    if (LOG.isDebugEnabled()) {
2191                        LOG.debug("getCreditMemoEncumbrance() difference: " + logItmNbr + " " + difference);
2192                    }
2193
2194                    SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine();
2195                    KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
2196                    if (amount == null) {
2197                        encumbranceAccountMap.put(acctString, difference);
2198                    } else {
2199                        encumbranceAccountMap.put(acctString, amount.add(difference));
2200                    }
2201                    lastAccount.setItemAccountOutstandingEncumbranceAmount(lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference));
2202                }
2203            }
2204        }
2205
2206        List<SourceAccountingLine> encumbranceAccounts = new ArrayList();
2207        for (Iterator iter = encumbranceAccountMap.keySet().iterator(); iter.hasNext(); ) {
2208            SourceAccountingLine acctString = (SourceAccountingLine) iter.next();
2209            KualiDecimal amount = (KualiDecimal) encumbranceAccountMap.get(acctString);
2210            if (amount.doubleValue() != 0) {
2211                acctString.setAmount(amount);
2212                encumbranceAccounts.add(acctString);
2213            }
2214        }
2215
2216        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
2217        //MSU Contribution OLEMI-8639 DTT-4009 OLECNTRB-966
2218        // SpringContext.getBean(BusinessObjectService.class).save(po);
2219
2220        return encumbranceAccounts;
2221    }
2222
2223    /**
2224     * Save the given general ledger entries
2225     *
2226     * @param glEntries List of GeneralLedgerPendingEntries to be saved
2227     */
2228    protected void saveGLEntries(List<GeneralLedgerPendingEntry> glEntries) {
2229        LOG.debug("saveGLEntries() started");
2230        businessObjectService.save(glEntries);
2231    }
2232
2233    /**
2234     * Save the given accounts for the given document.
2235     *
2236     * @param summaryAccounts         Accounts to be saved
2237     * @param purapDocumentIdentifier Purap document id for accounts
2238     */
2239    protected void saveAccountsPayableSummaryAccounts(List<SummaryAccount> summaryAccounts, Integer purapDocumentIdentifier, String docType) {
2240        LOG.debug("saveAccountsPayableSummaryAccounts() started");
2241        purapAccountingService.deleteSummaryAccounts(purapDocumentIdentifier, docType);
2242        List<AccountsPayableSummaryAccount> apSummaryAccounts = new ArrayList();
2243        for (SummaryAccount summaryAccount : summaryAccounts) {
2244            apSummaryAccounts.add(new AccountsPayableSummaryAccount(summaryAccount.getAccount(), purapDocumentIdentifier, docType));
2245        }
2246        businessObjectService.save(apSummaryAccounts);
2247    }
2248
2249    /**
2250     * Save the given accounts for the given document.
2251     *
2252     * @param summaryAccounts         Accounts to be saved
2253     * @param purapDocumentIdentifier Purap document id for accounts
2254     * @param docType                 Document Type Used
2255     */
2256    protected void saveInvoiceAccountsPayableSummaryAccounts(List<SummaryAccount> summaryAccounts, Integer purapDocumentIdentifier, String docType) {
2257        LOG.debug("saveAccountsPayableSummaryAccounts() started");
2258        purapAccountingService.deleteSummaryAccounts(purapDocumentIdentifier, docType);
2259        List<OleInvoiceAccountsPayableSummaryAccount> apSummaryAccounts = new ArrayList();
2260        for (SummaryAccount summaryAccount : summaryAccounts) {
2261            apSummaryAccounts.add(new OleInvoiceAccountsPayableSummaryAccount(summaryAccount.getAccount(), purapDocumentIdentifier, docType));
2262        }
2263        businessObjectService.save(apSummaryAccounts);
2264    }
2265
2266    /**
2267     * Find item in PO based on given parameters. Must send either the line # or item type.
2268     *
2269     * @param po       Purchase Order containing list of items
2270     * @param nbr      Line # of desired item (could be null)
2271     * @param itemType Item type of desired item
2272     * @return PurcahseOrderItem found matching given criteria
2273     */
2274    protected PurchaseOrderItem getPoItem(PurchaseOrderDocument po, Integer nbr, ItemType itemType) {
2275        LOG.debug("getPoItem() started");
2276        for (Iterator iter = po.getItems().iterator(); iter.hasNext(); ) {
2277            PurchaseOrderItem element = (PurchaseOrderItem) iter.next();
2278            if (itemType.isLineItemIndicator()) {
2279                if (ObjectUtils.isNotNull(nbr) && ObjectUtils.isNotNull(element.getItemLineNumber()) && (nbr.compareTo(element.getItemLineNumber()) == 0)) {
2280                    return element;
2281                }
2282            } else {
2283                if (element.getItemTypeCode().equals(itemType.getItemTypeCode())) {
2284                    return element;
2285                }
2286            }
2287        }
2288        return null;
2289    }
2290
2291    /**
2292     * Format description for general ledger entry. Currently making sure length is less than 40 char.
2293     *
2294     * @param description String to be formatted
2295     * @return Formatted String
2296     */
2297    protected String entryDescription(String description) {
2298        if (description != null && description.length() > 40) {
2299            return description.toString().substring(0, 39);
2300        } else {
2301            return description;
2302        }
2303    }
2304
2305    /**
2306     * Calculate quantity change for creating Credit Memo entries
2307     *
2308     * @param cancel     Boolean indicating whether entries are for creation or cancellation of credit memo
2309     * @param poItem     Purchase Order Item
2310     * @param cmQuantity Quantity on credit memo item
2311     * @return Calculated change
2312     */
2313    protected KualiDecimal calculateQuantityChange(boolean cancel, PurchaseOrderItem poItem, KualiDecimal cmQuantity) {
2314        LOG.debug("calculateQuantityChange() started");
2315
2316        // Calculate quantity change & adjust invoiced quantity & outstanding encumbered quantity
2317        KualiDecimal encumbranceQuantityChange = null;
2318        if (cancel) {
2319            encumbranceQuantityChange = cmQuantity.multiply(new KualiDecimal("-1"));
2320        } else {
2321            encumbranceQuantityChange = cmQuantity;
2322        }
2323        poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity().subtract(encumbranceQuantityChange));
2324        poItem.setItemOutstandingEncumberedQuantity(poItem.getItemOutstandingEncumberedQuantity().add(encumbranceQuantityChange));
2325
2326        // Check for overflows
2327        if (cancel) {
2328            if (poItem.getItemOutstandingEncumberedQuantity().doubleValue() < 0) {
2329                LOG.debug("calculateQuantityChange() Cancel overflow");
2330                KualiDecimal difference = poItem.getItemOutstandingEncumberedQuantity().abs();
2331                poItem.setItemOutstandingEncumberedQuantity(ZERO);
2332                poItem.setItemInvoicedTotalQuantity(poItem.getItemQuantity());
2333                encumbranceQuantityChange = encumbranceQuantityChange.add(difference);
2334            }
2335        }
2336// Coomented for JIRA:OLE-5162 as encumbrance get doubled here.
2337       /* else {
2338            if (poItem.getItemInvoicedTotalQuantity().doubleValue() < 0) {
2339                LOG.debug("calculateQuantityChange() Create overflow");
2340                KualiDecimal difference = poItem.getItemInvoicedTotalQuantity().abs();
2341                poItem.setItemOutstandingEncumberedQuantity(poItem.getItemQuantity());
2342                poItem.setItemInvoicedTotalQuantity(ZERO);
2343                encumbranceQuantityChange = encumbranceQuantityChange.add(difference);
2344            }
2345        } */
2346        return encumbranceQuantityChange;
2347    }
2348
2349    public void setDateTimeService(DateTimeService dateTimeService) {
2350        this.dateTimeService = dateTimeService;
2351    }
2352
2353    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
2354        this.businessObjectService = businessObjectService;
2355    }
2356
2357    public void setGeneralLedgerPendingEntryService(GeneralLedgerPendingEntryService generalLedgerPendingEntryService) {
2358        this.generalLedgerPendingEntryService = generalLedgerPendingEntryService;
2359    }
2360
2361    public void setKualiRuleService(KualiRuleService kualiRuleService) {
2362        this.kualiRuleService = kualiRuleService;
2363    }
2364
2365    public void setPurapAccountingService(PurapAccountingService purapAccountingService) {
2366        this.purapAccountingService = purapAccountingService;
2367    }
2368
2369    public void setUniversityDateService(UniversityDateService universityDateService) {
2370        this.universityDateService = universityDateService;
2371    }
2372
2373    public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
2374        this.purchaseOrderService = purchaseOrderService;
2375    }
2376
2377    public void setObjectCodeService(ObjectCodeService objectCodeService) {
2378        this.objectCodeService = objectCodeService;
2379    }
2380
2381    public void setSubObjectCodeService(SubObjectCodeService subObjectCodeService) {
2382        this.subObjectCodeService = subObjectCodeService;
2383    }
2384
2385    public void setParameterService(ParameterService parameterService) {
2386        this.parameterService = parameterService;
2387    }
2388
2389    public void setPaymentRequestService(PaymentRequestService paymentRequestService) {
2390        this.paymentRequestService = paymentRequestService;
2391    }
2392
2393    /**
2394     * Sets the invoiceService attribute value.
2395     *
2396     * @param invoiceService The invoiceService to set.
2397     */
2398
2399    public void setInvoiceService(InvoiceService invoiceService) {
2400        this.invoiceService = invoiceService;
2401    }
2402
2403
2404}