001: /*
002: * Copyright 2006-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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: */
016: package org.kuali.module.financial.web.struts.form;
017:
018: import static org.kuali.kfs.KFSConstants.VOUCHER_LINE_HELPER_CREDIT_PROPERTY_NAME;
019: import static org.kuali.kfs.KFSConstants.VOUCHER_LINE_HELPER_DEBIT_PROPERTY_NAME;
020:
021: import java.sql.Date;
022: import java.util.ArrayList;
023: import java.util.List;
024:
025: import javax.servlet.http.HttpServletRequest;
026:
027: import org.apache.commons.lang.StringUtils;
028: import org.kuali.core.document.AmountTotaling;
029: import org.kuali.core.service.DateTimeService;
030: import org.kuali.core.util.GlobalVariables;
031: import org.kuali.core.util.KualiDecimal;
032: import org.kuali.core.util.ObjectUtils;
033: import org.kuali.core.web.format.CurrencyFormatter;
034: import org.kuali.kfs.KFSConstants;
035: import org.kuali.kfs.KFSKeyConstants;
036: import org.kuali.kfs.KFSPropertyConstants;
037: import org.kuali.kfs.bo.SourceAccountingLine;
038: import org.kuali.kfs.context.SpringContext;
039: import org.kuali.kfs.web.struts.form.KualiAccountingDocumentFormBase;
040: import org.kuali.module.chart.bo.AccountingPeriod;
041: import org.kuali.module.chart.bo.ObjectCode;
042: import org.kuali.module.chart.bo.SubObjCd;
043: import org.kuali.module.chart.service.AccountingPeriodService;
044: import org.kuali.module.financial.bo.VoucherAccountingLineHelper;
045: import org.kuali.module.financial.bo.VoucherAccountingLineHelperBase;
046: import org.kuali.module.financial.document.VoucherDocument;
047:
048: /**
049: * This class is the Struts specific form object that works in conjunction with the pojo utilities to build the UI for Voucher
050: * Document instances. This class is unique in that it leverages a helper data structure called the
051: * <code>{@link VoucherAccountingLineHelper}</code> because Voucher documents, under some/none conditions, presents the user with
052: * a debit and credit column for amount entry. New accounting lines use specific credit and debit amount fields b/c the new line is
053: * explicitly known; however, already existing accounting lines need to exist within a list with ordering that matches the
054: * accounting lines source list.
055: */
056: public class VoucherForm extends KualiAccountingDocumentFormBase {
057: private List accountingPeriods;
058: private KualiDecimal newSourceLineDebit;
059: private KualiDecimal newSourceLineCredit;
060: private List voucherLineHelpers;
061: protected String selectedAccountingPeriod;
062:
063: /**
064: * Supplements a constructor for this voucher class
065: */
066: public VoucherForm() {
067: populateDefaultSelectedAccountingPeriod();
068: setNewSourceLineCredit(KualiDecimal.ZERO);
069: setNewSourceLineDebit(KualiDecimal.ZERO);
070: setVoucherLineHelpers(new ArrayList());
071: }
072:
073: /**
074: * sets initial selected accounting period to current period
075: */
076: private void populateDefaultSelectedAccountingPeriod() {
077: Date date = SpringContext.getBean(DateTimeService.class)
078: .getCurrentSqlDate();
079: AccountingPeriod accountingPeriod = SpringContext.getBean(
080: AccountingPeriodService.class).getByDate(date);
081:
082: StringBuffer sb = new StringBuffer();
083: sb.append(accountingPeriod.getUniversityFiscalPeriodCode());
084: sb.append(accountingPeriod.getUniversityFiscalYear());
085:
086: setSelectedAccountingPeriod(sb.toString());
087: }
088:
089: /**
090: * Overrides the parent to call super.populate and then to call the two methods that are specific to loading the two select
091: * lists on the page. In addition, this also makes sure that the credit and debit amounts are filled in for situations where
092: * validation errors occur and the page reposts.
093: *
094: * @see org.kuali.core.web.struts.pojo.PojoForm#populate(javax.servlet.http.HttpServletRequest)
095: */
096: @Override
097: public void populate(HttpServletRequest request) {
098: super .populate(request);
099:
100: // populate the drop downs
101: if (KFSConstants.RETURN_METHOD_TO_CALL
102: .equals(getMethodToCall())) {
103: String selectedPeriod = (StringUtils
104: .defaultString(request
105: .getParameter(KFSPropertyConstants.UNIVERSITY_FISCAL_PERIOD_CODE)) + StringUtils
106: .defaultString(request
107: .getParameter(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR)));
108: if (StringUtils.isNotBlank(selectedPeriod)) {
109: setSelectedAccountingPeriod(selectedPeriod);
110: }
111: }
112: populateAccountingPeriodListForRendering();
113:
114: // we don't want to do this if we are just reloading the document
115: if (StringUtils.isBlank(getMethodToCall())
116: || !getMethodToCall().equals(
117: KFSConstants.RELOAD_METHOD_TO_CALL)) {
118: // make sure the amount fields are populated appropriately when in debit/credit amount mode
119: populateCreditAndDebitAmounts();
120: }
121: }
122:
123: /**
124: * util method to get postingYear out of selectedAccountingPeriod
125: *
126: * @return Integer
127: */
128:
129: private Integer getSelectedPostingYear() {
130: Integer postingYear = null;
131: if (StringUtils.isNotBlank(getSelectedAccountingPeriod())) {
132: postingYear = new Integer(StringUtils.right(
133: getSelectedAccountingPeriod(), 4));
134: }
135: return postingYear;
136: }
137:
138: /**
139: * util method to get posting period code out of selectedAccountingPeriod
140: *
141: * @return String
142: */
143: private String getSelectedPostingPeriodCode() {
144: String periodCode = null;
145: String selectedPeriod = getSelectedAccountingPeriod();
146: if (StringUtils.isNotBlank(selectedPeriod)) {
147: periodCode = StringUtils.left(selectedPeriod, 2);
148: }
149: return periodCode;
150: }
151:
152: /**
153: * Helper method to make casting easier
154: *
155: * @return VoucherDocument
156: */
157: public VoucherDocument getVoucherDocument() {
158: return (VoucherDocument) getDocument();
159: }
160:
161: /**
162: * Override the parent, to push the chosen accounting period and balance type down into the source accounting line object. In
163: * addition, check the balance type to see if it's the "External Encumbrance" balance and alter the encumbrance update code on
164: * the accounting line appropriately.
165: *
166: * @see org.kuali.core.web.struts.form.KualiTransactionalDocumentFormBase#populateSourceAccountingLine(org.kuali.core.bo.SourceAccountingLine)
167: */
168: @Override
169: public void populateSourceAccountingLine(
170: SourceAccountingLine sourceLine) {
171: super .populateSourceAccountingLine(sourceLine);
172:
173: // set the chosen accounting period into the line
174: String selectedAccountingPeriod = getSelectedAccountingPeriod();
175:
176: if (StringUtils.isNotBlank(selectedAccountingPeriod)) {
177: Integer postingYear = getSelectedPostingYear();
178: sourceLine.setPostingYear(postingYear);
179:
180: if (ObjectUtils.isNull(sourceLine.getObjectCode())) {
181: sourceLine.setObjectCode(new ObjectCode());
182: }
183: sourceLine.getObjectCode().setUniversityFiscalYear(
184: postingYear);
185:
186: if (ObjectUtils.isNull(sourceLine.getSubObjectCode())) {
187: sourceLine.setSubObjectCode(new SubObjCd());
188: }
189: sourceLine.getSubObjectCode().setUniversityFiscalYear(
190: postingYear);
191: }
192:
193: }
194:
195: /**
196: * This method retrieves the list of valid accounting periods to display.
197: *
198: * @return List
199: */
200: public List getAccountingPeriods() {
201: return accountingPeriods;
202: }
203:
204: /**
205: * This method sets the list of valid accounting periods to display.
206: *
207: * @param accountingPeriods
208: */
209: public void setAccountingPeriods(List accountingPeriods) {
210: this .accountingPeriods = accountingPeriods;
211: }
212:
213: /**
214: * This method returns the reversal date in the format MMM d, yyyy.
215: *
216: * @return String
217: */
218: public String getFormattedReversalDate() {
219: return formatReversalDate(getVoucherDocument()
220: .getReversalDate());
221: }
222:
223: /**
224: * This method retrieves the selectedAccountingPeriod.
225: *
226: * @return String
227: */
228: public String getSelectedAccountingPeriod() {
229: return selectedAccountingPeriod;
230: }
231:
232: /**
233: * @return AccountingPeriod associated with the currently selected period
234: */
235: public AccountingPeriod getAccountingPeriod() {
236: AccountingPeriod period = null;
237:
238: if (!StringUtils.isBlank(getSelectedAccountingPeriod())) {
239: period = SpringContext.getBean(
240: AccountingPeriodService.class).getByPeriod(
241: getSelectedPostingPeriodCode(),
242: getSelectedPostingYear());
243: }
244:
245: return period;
246: }
247:
248: /**
249: * This method sets the selectedAccountingPeriod.
250: *
251: * @param selectedAccountingPeriod
252: */
253: public void setSelectedAccountingPeriod(
254: String selectedAccountingPeriod) {
255: this .selectedAccountingPeriod = selectedAccountingPeriod;
256: }
257:
258: /**
259: * Accessor to the list of <code>{@link VoucherAccountingLineHelper}</code> instances. This method retrieves the list of
260: * helper line objects for the form.
261: *
262: * @return List
263: */
264: public List getVoucherLineHelpers() {
265: return voucherLineHelpers;
266: }
267:
268: /**
269: * This method retrieves the proper voucher helper line data structure at the passed in list index so that it matches up with
270: * the correct accounting line at that index.
271: *
272: * @param index
273: * @return VoucherAccountingLineHelper
274: */
275: public VoucherAccountingLineHelper getVoucherLineHelper(int index) {
276: while (getVoucherLineHelpers().size() <= index) {
277: getVoucherLineHelpers().add(
278: new VoucherAccountingLineHelperBase());
279: }
280: return (VoucherAccountingLineHelper) getVoucherLineHelpers()
281: .get(index);
282: }
283:
284: /**
285: * This method sets the list of helper lines for the form.
286: *
287: * @param voucherLineHelpers
288: */
289: public void setVoucherLineHelpers(List voucherLineHelpers) {
290: this .voucherLineHelpers = voucherLineHelpers;
291: }
292:
293: /**
294: * This method retrieves the credit amount of the new accounting line that was added.
295: *
296: * @return KualiDecimal
297: */
298: public KualiDecimal getNewSourceLineCredit() {
299: return newSourceLineCredit;
300: }
301:
302: /**
303: * This method sets the credit amount of the new accounting line that was added.
304: *
305: * @param newSourceLineCredit
306: */
307: public void setNewSourceLineCredit(KualiDecimal newSourceLineCredit) {
308: this .newSourceLineCredit = newSourceLineCredit;
309: }
310:
311: /**
312: * This method retrieves the debit amount of the new accounting line that was added.
313: *
314: * @return KualiDecimal
315: */
316: public KualiDecimal getNewSourceLineDebit() {
317: return newSourceLineDebit;
318: }
319:
320: /**
321: * This method sets the debit amount of the new accounting line that was added.
322: *
323: * @param newSourceLineDebit
324: */
325: public void setNewSourceLineDebit(KualiDecimal newSourceLineDebit) {
326: this .newSourceLineDebit = newSourceLineDebit;
327: }
328:
329: /**
330: * This method retrieves the voucher's debit total formatted as currency.
331: *
332: * @return String
333: */
334: public String getCurrencyFormattedDebitTotal() {
335: return (String) new CurrencyFormatter()
336: .format(getVoucherDocument().getDebitTotal());
337: }
338:
339: /**
340: * This method retrieves the voucher's credit total formatted as currency.
341: *
342: * @return String
343: */
344: public String getCurrencyFormattedCreditTotal() {
345: return (String) new CurrencyFormatter()
346: .format(getVoucherDocument().getCreditTotal());
347: }
348:
349: /**
350: * This method retrieves the voucher's total formatted as currency.
351: *
352: * @return String
353: */
354: public String getCurrencyFormattedTotal() {
355: return (String) new CurrencyFormatter()
356: .format(((AmountTotaling) getVoucherDocument())
357: .getTotalDollarAmount());
358: }
359:
360: /**
361: * This method retrieves all of the "open for posting" accounting periods and prepares them to be rendered in a dropdown UI
362: * component.
363: */
364: protected void populateAccountingPeriodListForRendering() {
365: // grab the list of valid accounting periods
366: ArrayList accountingPeriods = new ArrayList(SpringContext
367: .getBean(AccountingPeriodService.class)
368: .getOpenAccountingPeriods());
369: // set into the form for rendering
370: setAccountingPeriods(accountingPeriods);
371: // set the chosen accounting period into the form
372: populateSelectedVoucherAccountingPeriod();
373: }
374:
375: /**
376: * This method parses the accounting period value from the form and builds a basic AccountingPeriod object so that the voucher
377: * is properly persisted with the accounting period set for it.
378: */
379: protected void populateSelectedVoucherAccountingPeriod() {
380: if (StringUtils.isNotBlank(getSelectedAccountingPeriod())) {
381: AccountingPeriod ap = new AccountingPeriod();
382: ap
383: .setUniversityFiscalPeriodCode(getSelectedPostingPeriodCode());
384: ap.setUniversityFiscalYear(getSelectedPostingYear());
385: getFinancialDocument().setAccountingPeriod(ap);
386: }
387: }
388:
389: /**
390: * If the balance type is an offset generation balance type, then the user is able to enter the amount as either a debit or a
391: * credit, otherwise, they only need to deal with the amount field in this case we always need to update the underlying bo so
392: * that the debit/credit code along with the amount, is properly set.
393: */
394: protected void populateCreditAndDebitAmounts() {
395: processDebitAndCreditForNewSourceLine();
396: processDebitAndCreditForAllSourceLines();
397: }
398:
399: /**
400: * This method uses the newly entered debit and credit amounts to populate the new source line that is to be added to the
401: * voucher document.
402: *
403: * @return boolean True if the processing was successful, false otherwise.
404: */
405: protected boolean processDebitAndCreditForNewSourceLine() {
406: // using debits and credits supplied, populate the new source accounting line's amount and debit/credit code appropriately
407: boolean passed = processDebitAndCreditForSourceLine(
408: getNewSourceLine(), newSourceLineDebit,
409: newSourceLineCredit, KFSConstants.NEGATIVE_ONE);
410:
411: return passed;
412: }
413:
414: /**
415: * This method iterates through all of the source accounting lines associated with the voucher doc and accounts for any changes
416: * to the credit and debit amounts, populate the source lines' amount and debit/credit code fields appropriately, so that they
417: * can be persisted accurately. This accounts for the fact that users may change the amounts and/or flip-flop the credit debit
418: * amounts on any accounting line after the initial add of the accounting line.
419: *
420: * @return boolean
421: */
422: protected boolean processDebitAndCreditForAllSourceLines() {
423: VoucherDocument vDoc = getVoucherDocument();
424:
425: // iterate through all of the source accounting lines
426: boolean validProcessing = true;
427: for (int i = 0; i < vDoc.getSourceAccountingLines().size(); i++) {
428: // retrieve the proper business objects from the form
429: SourceAccountingLine sourceLine = vDoc
430: .getSourceAccountingLine(i);
431: VoucherAccountingLineHelper voucherLineHelper = getVoucherLineHelper(i);
432:
433: // now process the amounts and the line
434: // we want to process all lines, some may be invalid b/c of dual amount values, but this method will handle
435: // only processing the valid ones, that way we are guaranteed that values in the valid lines carry over through the
436: // post and invalid ones do not alter the underlying business object
437: validProcessing &= processDebitAndCreditForSourceLine(
438: sourceLine, voucherLineHelper.getDebit(),
439: voucherLineHelper.getCredit(), i);
440: }
441: return validProcessing;
442: }
443:
444: /**
445: * This method checks the debit and credit attributes passed in, figures out which one has a value, and sets the source
446: * accounting line's amount and debit/credit attribute appropriately. It assumes that if it finds something in the debit field,
447: * it's a debit entry, otherwise it's a credit entry. If a user enters a value into both fields, it will assume the debit value,
448: * then when the br eval framework applies the "add" rule, it will bomb out. If first checks to make sure that there isn't a
449: * value in both the credit and debit columns.
450: *
451: * @param sourceLine
452: * @param debitAmount
453: * @param creditAmount
454: * @param index if -1, then its a new line, if not -1 then it's an existing line
455: * @return boolean True if the processing was successful, false otherwise.
456: */
457: protected boolean processDebitAndCreditForSourceLine(
458: SourceAccountingLine sourceLine, KualiDecimal debitAmount,
459: KualiDecimal creditAmount, int index) {
460: // check to make sure that the
461: if (!validateCreditAndDebitAmounts(debitAmount, creditAmount,
462: index)) {
463: return false;
464: }
465:
466: // check to see which amount field has a value - credit or debit field?
467: // and set the values of the appropriate fields
468: if (debitAmount != null && debitAmount.isNonZero()) { // a value entered into the debit field? if so it's a debit
469: // create a new instance w/out reference
470: KualiDecimal tmpDebitAmount = new KualiDecimal(debitAmount
471: .toString());
472: sourceLine.setDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
473: sourceLine.setAmount(tmpDebitAmount);
474: } else if (creditAmount != null && creditAmount.isNonZero()) { // assume credit, if both are set the br eval framework will
475: // catch it
476: KualiDecimal tmpCreditAmount = new KualiDecimal(
477: creditAmount.toString());
478: sourceLine.setDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
479: sourceLine.setAmount(tmpCreditAmount);
480: } else { // default to DEBIT, note the br eval framework will still pick it up
481: sourceLine.setDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
482: sourceLine.setAmount(KualiDecimal.ZERO);
483: }
484:
485: return true;
486: }
487:
488: /**
489: * This method checks to make sure that there isn't a value in both the credit and debit columns for a given accounting line.
490: *
491: * @param creditAmount
492: * @param debitAmount
493: * @param index if -1, it's a new line, if not -1, then its an existing line
494: * @return boolean False if both the credit and debit fields have a value, true otherwise.
495: */
496: protected boolean validateCreditAndDebitAmounts(
497: KualiDecimal debitAmount, KualiDecimal creditAmount,
498: int index) {
499: boolean valid = false;
500: if (null != creditAmount && null != debitAmount) {
501: if (creditAmount.isNonZero() && debitAmount.isNonZero()) {
502: // there's a value in both fields
503: if (KFSConstants.NEGATIVE_ONE == index) { // it's a new line
504: GlobalVariables
505: .getErrorMap()
506: .putErrorWithoutFullErrorPath(
507: KFSConstants.DEBIT_AMOUNT_PROPERTY_NAME,
508: KFSKeyConstants.ERROR_DOCUMENT_JV_AMOUNTS_IN_CREDIT_AND_DEBIT_FIELDS);
509: GlobalVariables
510: .getErrorMap()
511: .putErrorWithoutFullErrorPath(
512: KFSConstants.CREDIT_AMOUNT_PROPERTY_NAME,
513: KFSKeyConstants.ERROR_DOCUMENT_JV_AMOUNTS_IN_CREDIT_AND_DEBIT_FIELDS);
514: } else {
515: String errorKeyPath = KFSConstants.JOURNAL_LINE_HELPER_PROPERTY_NAME
516: + KFSConstants.SQUARE_BRACKET_LEFT
517: + Integer.toString(index)
518: + KFSConstants.SQUARE_BRACKET_RIGHT;
519: GlobalVariables
520: .getErrorMap()
521: .putErrorWithoutFullErrorPath(
522: errorKeyPath
523: + VOUCHER_LINE_HELPER_DEBIT_PROPERTY_NAME,
524: KFSKeyConstants.ERROR_DOCUMENT_JV_AMOUNTS_IN_CREDIT_AND_DEBIT_FIELDS);
525: GlobalVariables
526: .getErrorMap()
527: .putErrorWithoutFullErrorPath(
528: errorKeyPath
529: + VOUCHER_LINE_HELPER_CREDIT_PROPERTY_NAME,
530: KFSKeyConstants.ERROR_DOCUMENT_JV_AMOUNTS_IN_CREDIT_AND_DEBIT_FIELDS);
531: }
532: } else {
533: valid = true;
534: }
535: } else {
536: valid = true;
537: }
538: return valid;
539: }
540: }
|