001/* 002 * Copyright 2005 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.fp.document; 017 018import static org.kuali.ole.sys.OLEConstants.EMPTY_STRING; 019import static org.kuali.ole.sys.OLEConstants.GL_CREDIT_CODE; 020import static org.kuali.ole.sys.OLEConstants.GL_DEBIT_CODE; 021import static org.kuali.ole.sys.OLEPropertyConstants.BALANCE_TYPE; 022 023import java.util.ArrayList; 024import java.util.Iterator; 025import java.util.List; 026 027import org.apache.commons.lang.StringUtils; 028import org.kuali.ole.coa.businessobject.BalanceType; 029import org.kuali.ole.fp.businessobject.JournalVoucherAccountingLineParser; 030import org.kuali.ole.fp.businessobject.VoucherSourceAccountingLine; 031import org.kuali.ole.sys.OLEConstants; 032import org.kuali.ole.sys.businessobject.AccountingLine; 033import org.kuali.ole.sys.businessobject.AccountingLineBase; 034import org.kuali.ole.sys.businessobject.AccountingLineParser; 035import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry; 036import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper; 037import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntrySourceDetail; 038import org.kuali.ole.sys.businessobject.SourceAccountingLine; 039import org.kuali.ole.sys.businessobject.SufficientFundsItem; 040import org.kuali.ole.sys.context.SpringContext; 041import org.kuali.ole.sys.document.AccountingDocumentBase; 042import org.kuali.ole.sys.document.AmountTotaling; 043import org.kuali.ole.sys.document.Correctable; 044import org.kuali.ole.sys.document.service.DebitDeterminerService; 045import org.kuali.ole.sys.service.OptionsService; 046import org.kuali.rice.core.api.util.type.KualiDecimal; 047import org.kuali.rice.kew.api.exception.WorkflowException; 048import org.kuali.rice.krad.document.Copyable; 049 050/** 051 * This is the business object that represents the JournalVoucherDocument in Kuali. This is a transactional document that will 052 * eventually post transactions to the G/L. It integrates with workflow and contains a single group of accounting lines. The Journal 053 * Voucher is unique in that we only make use of one accounting line list: the source accounting lines seeing as a JV only records 054 * accounting lines as debits or credits. 055 */ 056public class JournalVoucherDocument extends AccountingDocumentBase implements VoucherDocument, Copyable, Correctable, AmountTotaling { 057 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(JournalVoucherDocument.class); 058 059 // document specific attributes 060 protected String balanceTypeCode; // balanceType key 061 protected BalanceType balanceType; 062 protected java.sql.Date reversalDate; 063 064 /** 065 * Constructs a JournalVoucherDocument instance. 066 */ 067 public JournalVoucherDocument() { 068 super(); 069 this.balanceType = new BalanceType(); 070 } 071 072 /** 073 * @see org.kuali.ole.sys.document.AccountingDocumentBase#checkSufficientFunds() 074 */ 075 @Override 076 public List<SufficientFundsItem> checkSufficientFunds() { 077 LOG.debug("checkSufficientFunds() started"); 078 079 // This document does not do sufficient funds checking 080 return new ArrayList<SufficientFundsItem>(); 081 } 082 083 /** 084 * @see org.kuali.ole.sys.document.AccountingDocumentBase#getSourceAccountingLineClass() 085 */ 086 @Override 087 public Class getSourceAccountingLineClass() { 088 return VoucherSourceAccountingLine.class; 089 } 090 091 /** 092 * This method retrieves the balance typ associated with this document. 093 * 094 * @return BalanceTyp 095 */ 096 public BalanceType getBalanceType() { 097 return balanceType; 098 } 099 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}