View Javadoc

1   /*
2    * Copyright 2005 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.fp.document;
17  
18  import static org.kuali.ole.sys.OLEConstants.EMPTY_STRING;
19  import static org.kuali.ole.sys.OLEConstants.GL_CREDIT_CODE;
20  import static org.kuali.ole.sys.OLEConstants.GL_DEBIT_CODE;
21  import static org.kuali.ole.sys.OLEPropertyConstants.BALANCE_TYPE;
22  
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.kuali.ole.coa.businessobject.BalanceType;
29  import org.kuali.ole.fp.businessobject.JournalVoucherAccountingLineParser;
30  import org.kuali.ole.fp.businessobject.VoucherSourceAccountingLine;
31  import org.kuali.ole.sys.OLEConstants;
32  import org.kuali.ole.sys.businessobject.AccountingLine;
33  import org.kuali.ole.sys.businessobject.AccountingLineBase;
34  import org.kuali.ole.sys.businessobject.AccountingLineParser;
35  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry;
36  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
37  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
38  import org.kuali.ole.sys.businessobject.SourceAccountingLine;
39  import org.kuali.ole.sys.businessobject.SufficientFundsItem;
40  import org.kuali.ole.sys.context.SpringContext;
41  import org.kuali.ole.sys.document.AccountingDocumentBase;
42  import org.kuali.ole.sys.document.AmountTotaling;
43  import org.kuali.ole.sys.document.Correctable;
44  import org.kuali.ole.sys.document.service.DebitDeterminerService;
45  import org.kuali.ole.sys.service.OptionsService;
46  import org.kuali.rice.core.api.util.type.KualiDecimal;
47  import org.kuali.rice.kew.api.exception.WorkflowException;
48  import org.kuali.rice.krad.document.Copyable;
49  
50  /**
51   * This is the business object that represents the JournalVoucherDocument in Kuali. This is a transactional document that will
52   * eventually post transactions to the G/L. It integrates with workflow and contains a single group of accounting lines. The Journal
53   * Voucher is unique in that we only make use of one accounting line list: the source accounting lines seeing as a JV only records
54   * accounting lines as debits or credits.
55   */
56  public class JournalVoucherDocument extends AccountingDocumentBase implements VoucherDocument, Copyable, Correctable, AmountTotaling {
57      protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(JournalVoucherDocument.class);
58  
59      // document specific attributes
60      protected String balanceTypeCode; // balanceType key
61      protected BalanceType balanceType;
62      protected java.sql.Date reversalDate;
63  
64      /**
65       * Constructs a JournalVoucherDocument instance.
66       */
67      public JournalVoucherDocument() {
68          super();
69          this.balanceType = new BalanceType();
70      }
71  
72      /**
73       * @see org.kuali.ole.sys.document.AccountingDocumentBase#checkSufficientFunds()
74       */
75      @Override
76      public List<SufficientFundsItem> checkSufficientFunds() {
77          LOG.debug("checkSufficientFunds() started");
78  
79          // This document does not do sufficient funds checking
80          return new ArrayList<SufficientFundsItem>();
81      }
82  
83      /**
84       * @see org.kuali.ole.sys.document.AccountingDocumentBase#getSourceAccountingLineClass()
85       */
86      @Override
87      public Class getSourceAccountingLineClass() {
88          return VoucherSourceAccountingLine.class;
89      }
90  
91      /**
92       * This method retrieves the balance typ associated with this document.
93       * 
94       * @return BalanceTyp
95       */
96      public BalanceType getBalanceType() {
97          return balanceType;
98      }
99  
100     /**
101      * This method sets the balance type associated with this document.
102      * 
103      * @param balanceType
104      * @deprecated
105      */
106     @Deprecated
107     public void setBalanceType(BalanceType balanceType) {
108         this.balanceType = balanceType;
109     }
110 
111     /**
112      * Gets the balanceTypeCode attribute.
113      * 
114      * @return Returns the balanceTypeCode.
115      */
116     public String getBalanceTypeCode() {
117         return balanceTypeCode;
118     }
119 
120     /**
121      * Sets the balanceTypeCode attribute value.
122      * 
123      * @param balanceTypeCode The balanceTypeCode to set.
124      */
125     public void setBalanceTypeCode(String balanceTypeCode) {
126         this.balanceTypeCode = balanceTypeCode;
127     }
128 
129     /**
130      * This method retrieves the reversal date associated with this document.
131      * 
132      * @return java.sql.Date
133      */
134     public java.sql.Date getReversalDate() {
135         return reversalDate;
136     }
137 
138     /**
139      * This method sets the reversal date associated with this document.
140      * 
141      * @param reversalDate
142      */
143     public void setReversalDate(java.sql.Date reversalDate) {
144         this.reversalDate = reversalDate;
145     }
146 
147     /**
148      * Overrides the base implementation to return an empty string.
149      * 
150      * @return String
151      */
152     @Override
153     public String getSourceAccountingLinesSectionTitle() {
154         return EMPTY_STRING;
155     }
156 
157     /**
158      * Overrides the base implementation to return an empty string.
159      * 
160      * @return String
161      */
162     @Override
163     public String getTargetAccountingLinesSectionTitle() {
164         return EMPTY_STRING;
165     }
166 
167     /**
168      * This method calculates the debit total for a JV document keying off of the debit/debit code, only summing the accounting
169      * lines with a debitDebitCode that matched the debit constant, and returns the results.
170      * 
171      * @return KualiDecimal
172      */
173     public KualiDecimal getDebitTotal() {
174         KualiDecimal debitTotal = KualiDecimal.ZERO;
175         AccountingLineBase al = null;
176         Iterator iter = sourceAccountingLines.iterator();
177         while (iter.hasNext()) {
178             al = (AccountingLineBase) iter.next();
179             if (StringUtils.isNotBlank(al.getDebitCreditCode()) && al.getDebitCreditCode().equals(GL_DEBIT_CODE)) {
180                 debitTotal = debitTotal.add(al.getAmount());
181             }
182         }
183 
184         return debitTotal;
185     }
186 
187     /**
188      * This method calculates the credit total for a JV document keying off of the debit/credit code, only summing the accounting
189      * lines with a debitCreditCode that matched the debit constant, and returns the results.
190      * 
191      * @return KualiDecimal
192      */
193     public KualiDecimal getCreditTotal() {
194         KualiDecimal creditTotal = KualiDecimal.ZERO;
195         AccountingLineBase al = null;
196         Iterator iter = sourceAccountingLines.iterator();
197         while (iter.hasNext()) {
198             al = (AccountingLineBase) iter.next();
199             if (StringUtils.isNotBlank(al.getDebitCreditCode()) && al.getDebitCreditCode().equals(GL_CREDIT_CODE)) {
200                 creditTotal = creditTotal.add(al.getAmount());
201             }
202         }
203         return creditTotal;
204     }
205 
206     /**
207      * This method determines the "total" for the JV document. If the selected balance type is an offset generation, then the method
208      * returns the total debits amount when it is greater than the total credit amount. otherwise, it returns total credit amount.
209      * When selected balance type is not an offset generation, the method returns the sum of all accounting line debit amounts.
210      * 
211      * @return KualiDecimal the total of the JV document.
212      */
213     public KualiDecimal getTotalDollarAmount() {
214 
215         KualiDecimal total = KualiDecimal.ZERO;
216 
217         this.refreshReferenceObject("balanceType");
218 
219         if (this.balanceType.isFinancialOffsetGenerationIndicator()) {
220             if (getCreditTotal().isGreaterThan(getDebitTotal())) {
221                 total = getCreditTotal();
222             }
223             else {
224                 total = getDebitTotal();
225             }
226         }
227         else {
228             total = getDebitTotal();
229         }
230         return total;
231     }
232 
233     /**
234      * Used to get the appropriate <code>{@link AccountingLineParser}</code> for the <code>Document</code>
235      * 
236      * @return AccountingLineParser
237      */
238     @Override
239     public AccountingLineParser getAccountingLineParser() {
240         return new JournalVoucherAccountingLineParser(getBalanceTypeCode());
241     }
242 
243     /**
244      * @see org.kuali.ole.sys.document.AccountingDocumentBase#toErrorCorrection()
245      */
246     @Override
247     public void toErrorCorrection() throws WorkflowException {
248         super.toErrorCorrection();
249         processJournalVoucherErrorCorrections();
250     }
251 
252     /**
253      * This method checks to make sure that the JV that we are dealing with was one that was created in debit/credit mode, not
254      * single amount entry mode. If this is a debit/credit JV, then iterate over each source line and flip the sign on the amount to
255      * nullify the super's effect, then flip the debit/credit code b/c an error corrected JV flips the debit/credit code.
256      */
257     protected void processJournalVoucherErrorCorrections() {
258         Iterator i = getSourceAccountingLines().iterator();
259 
260         this.refreshReferenceObject(BALANCE_TYPE);
261 
262         if (this.getBalanceType().isFinancialOffsetGenerationIndicator()) { // make sure this is not a single amount entered JV
263             int index = 0;
264             while (i.hasNext()) {
265                 SourceAccountingLine sLine = (SourceAccountingLine) i.next();
266 
267                 String debitCreditCode = sLine.getDebitCreditCode();
268 
269                 if (StringUtils.isNotBlank(debitCreditCode)) {
270                     // negate the amount to to nullify the effects of the super, b/c super flipped it the first time through
271                     sLine.setAmount(sLine.getAmount().negated()); // offsets the effect the super
272 
273                     // now just flip the debit/credit code
274                     if (GL_DEBIT_CODE.equals(debitCreditCode)) {
275                         sLine.setDebitCreditCode(GL_CREDIT_CODE);
276                     }
277                     else if (GL_CREDIT_CODE.equals(debitCreditCode)) {
278                         sLine.setDebitCreditCode(GL_DEBIT_CODE);
279                     }
280                     else {
281                         throw new IllegalStateException("SourceAccountingLine at index " + index + " does not have a debit/credit " + "code associated with it.  This should never have occured. Please contact your system administrator.");
282 
283                     }
284                     index++;
285                 }
286             }
287         }
288     }
289     
290     /**
291      * The following are credits (return false)
292      * <ol>
293      * <li> (debitCreditCode isNotBlank) && debitCreditCode != 'D'
294      * </ol>
295      * 
296      * The following are debits (return true)
297      * <ol>
298      * <li> debitCreditCode == 'D'
299      * <li> debitCreditCode isBlank
300      * </ol>
301      * 
302      * @param financialDocument The document which contains the accounting line being analyzed.
303      * @param accountingLine The accounting line which will be analyzed to determine if it is a debit line.
304      * @return True if the accounting line provided is a debit accounting line, false otherwise.
305      * @throws IllegalStateException Thrown by method IsDebitUtiles.isDebitCode()
306      * 
307      * @see org.kuali.rice.krad.rule.AccountingLineRule#isDebit(org.kuali.rice.krad.document.FinancialDocument,
308      *      org.kuali.rice.krad.bo.AccountingLine)
309      * @see org.kuali.ole.sys.document.validation.impl.AccountingDocumentRuleBase.IsDebitUtils#isDebitCode(String)
310      */
311     public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) throws IllegalStateException {
312         AccountingLine accountingLine = (AccountingLine)postable;
313         String debitCreditCode = accountingLine.getDebitCreditCode();
314 
315         DebitDeterminerService isDebitUtils = SpringContext.getBean(DebitDeterminerService.class);
316         boolean isDebit = StringUtils.isBlank(debitCreditCode) || isDebitUtils.isDebitCode(debitCreditCode);
317 
318         return isDebit;
319     }
320     
321     /**
322      * This method sets attributes on the explicitly general ledger pending entry specific to JournalVoucher documents.
323      * This includes setting the accounting period code and year (as selected by the user, the object type code,
324      * the balance type code, the debit/credit code, the encumbrance update code and the reversal date.
325      * 
326      * @param financialDocument The document which contains the general ledger pending entry being modified.
327      * @param accountingLine The accounting line the explicit entry was generated from.
328      * @param explicitEntry The explicit entry being updated.
329      * 
330      * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.rice.krad.document.FinancialDocument,
331      *      org.kuali.rice.krad.bo.AccountingLine, org.kuali.module.gl.bo.GeneralLedgerPendingEntry)
332      */
333     @Override
334     public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry) {
335         AccountingLine accountingLine = (AccountingLine)postable;
336 
337         // set the appropriate accounting period values according to the values chosen by the user
338         explicitEntry.setUniversityFiscalPeriodCode(getPostingPeriodCode());
339         explicitEntry.setUniversityFiscalYear(getPostingYear());
340 
341         // set the object type code directly from what was entered in the interface
342         explicitEntry.setFinancialObjectTypeCode(accountingLine.getObjectTypeCode());
343 
344         // set the balance type code directly from what was entered in the interface
345         explicitEntry.setFinancialBalanceTypeCode(accountingLine.getBalanceTypeCode());
346 
347         // set the debit/credit code appropriately
348         refreshReferenceObject(BALANCE_TYPE);
349         if (getBalanceType().isFinancialOffsetGenerationIndicator()) {
350             explicitEntry.setTransactionDebitCreditCode(StringUtils.isNotBlank(accountingLine.getDebitCreditCode()) ? accountingLine.getDebitCreditCode() : OLEConstants.BLANK_SPACE);
351         }
352         else {
353             explicitEntry.setTransactionDebitCreditCode(OLEConstants.BLANK_SPACE);
354         }
355 
356         // set the encumbrance update code
357         explicitEntry.setTransactionEncumbranceUpdateCode(StringUtils.isNotBlank(accountingLine.getEncumbranceUpdateCode()) ? accountingLine.getEncumbranceUpdateCode() : OLEConstants.BLANK_SPACE);
358 
359         // set the reversal date to what what specified at the document level
360         if (getReversalDate() != null) {
361             explicitEntry.setFinancialDocumentReversalDate(getReversalDate());
362         }
363     }
364 
365     /**
366      * A Journal Voucher document doesn't generate an offset entry at all, so this method overrides to do nothing more than return
367      * true. This will be called by the parent's processGeneralLedgerPendingEntries method.
368      * 
369      * @param financialDocument The document the offset will be stored within.
370      * @param sequenceHelper The sequence object to be modified.
371      * @param accountingLineToCopy The accounting line the offset is generated for.
372      * @param explicitEntry The explicit entry the offset will be generated for.
373      * @param offsetEntry The offset entry to be processed.
374      * @return This method always returns true.
375      * 
376      * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#processOffsetGeneralLedgerPendingEntry(org.kuali.rice.krad.document.FinancialDocument,
377      *      org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper, org.kuali.rice.krad.bo.AccountingLine,
378      *      org.kuali.module.gl.bo.GeneralLedgerPendingEntry, org.kuali.module.gl.bo.GeneralLedgerPendingEntry)
379      */
380     @Override
381     public boolean processOffsetGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntry explicitEntry, GeneralLedgerPendingEntry offsetEntry) {
382         sequenceHelper.decrement(); // the parent already increments; assuming that all documents have offset entries
383         return true;
384     }
385     
386     /**
387      * 
388      * @see org.kuali.ole.sys.document.validation.impl.AccountingDocumentRuleBase#getGeneralLedgerPendingEntryAmountForAccountingLine(org.kuali.ole.sys.businessobject.AccountingLine)
389      */
390     @Override
391     public KualiDecimal getGeneralLedgerPendingEntryAmountForDetail(GeneralLedgerPendingEntrySourceDetail accountingLine) {
392         LOG.debug("getGeneralLedgerPendingEntryAmountForAccountingLine(AccountingLine) - start");
393         KualiDecimal returnKualiDecimal;
394 
395         String budgetCodes = SpringContext.getBean(OptionsService.class).getOptions(accountingLine.getPostingYear()).getBudgetCheckingBalanceTypeCd();
396         if (!this.balanceType.isFinancialOffsetGenerationIndicator()) {
397             returnKualiDecimal = accountingLine.getAmount();
398         }
399         else {
400             returnKualiDecimal = accountingLine.getAmount().abs();
401         }
402         LOG.debug("getGeneralLedgerPendingEntryAmountForAccountingLine(AccountingLine) - end");
403         return returnKualiDecimal;
404     }
405 }