001    /*
002     * Copyright 2011 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     */
016    package org.kuali.kfs.fp.document.web.struts;
017    
018    import java.io.FileNotFoundException;
019    import java.io.IOException;
020    import java.util.ArrayList;
021    
022    import javax.servlet.http.HttpServletRequest;
023    import javax.servlet.http.HttpServletResponse;
024    
025    import org.apache.commons.lang.StringUtils;
026    import org.apache.struts.action.ActionForm;
027    import org.apache.struts.action.ActionForward;
028    import org.apache.struts.action.ActionMapping;
029    import org.kuali.kfs.coa.businessobject.BalanceType;
030    import org.kuali.kfs.coa.service.BalanceTypeService;
031    import org.kuali.kfs.fp.businessobject.VoucherAccountingLineHelper;
032    import org.kuali.kfs.fp.businessobject.VoucherAccountingLineHelperBase;
033    import org.kuali.kfs.fp.businessobject.VoucherSourceAccountingLine;
034    import org.kuali.kfs.fp.document.JournalVoucherDocument;
035    import org.kuali.kfs.fp.document.VoucherDocument;
036    import org.kuali.kfs.sys.KFSConstants;
037    import org.kuali.kfs.sys.KFSKeyConstants;
038    import org.kuali.kfs.sys.KFSPropertyConstants;
039    import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
040    import org.kuali.kfs.sys.context.SpringContext;
041    import org.kuali.rice.kew.exception.WorkflowException;
042    import org.kuali.rice.kns.question.ConfirmationQuestion;
043    import org.kuali.rice.kns.service.KualiConfigurationService;
044    import org.kuali.rice.kns.util.GlobalVariables;
045    import org.kuali.rice.kns.util.KualiDecimal;
046    import org.kuali.rice.kns.web.format.CurrencyFormatter;
047    import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
048    
049    /**
050     * This class piggy backs on all of the functionality in the FinancialSystemTransactionalDocumentActionBase but is necessary for
051     * this document type. The Journal Voucher is unique in that it defines several fields that aren't typically used by the other
052     * financial transaction processing eDocs (i.e. external system fields, object type override, credit and debit amounts).
053     */
054    public class JournalVoucherAction extends VoucherAction {
055    
056        // used to determine which way the change balance type action is switching these are local constants only used within this
057        // action class these should not be used outside of this class
058        protected static final int CREDIT_DEBIT_TO_SINGLE_AMT_MODE = 0;
059        protected static final int SINGLE_AMT_TO_CREDIT_DEBIT_MODE = 1;
060        protected static final int EXT_ENCUMB_TO_NON_EXT_ENCUMB = 0;
061        protected static final int NON_EXT_ENCUMB_TO_EXT_ENCUMB = 1;
062        protected static final int NO_MODE_CHANGE = -1;
063    
064        /**
065         * Overrides the parent and then calls the super method after building the array lists for valid accounting periods and balance
066         * types.
067         * 
068         * @see org.kuali.rice.kns.web.struts.action.KualiAction#execute(ActionMapping mapping, ActionForm form, HttpServletRequest
069         *      request, HttpServletResponse response)
070         */
071        @Override
072        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
073            JournalVoucherForm journalVoucherForm = (JournalVoucherForm) form;
074    
075            populateBalanceTypeOneDocument(journalVoucherForm);
076    
077            // now check to see if the balance type was changed and if so, we want to
078            // set the method to call so that the appropriate action can be invoked
079            // had to do it this way b/c the changing of the drop down causes the page to re-submit
080            // and couldn't use a hidden field called "methodToCall" b/c it screwed everything up
081            ActionForward returnForward;
082            if (StringUtils.isNotBlank(journalVoucherForm.getOriginalBalanceType()) && !journalVoucherForm.getSelectedBalanceType().getCode().equals(journalVoucherForm.getOriginalBalanceType())) {
083                returnForward = super.dispatchMethod(mapping, form, request, response, KFSConstants.CHANGE_JOURNAL_VOUCHER_BALANCE_TYPE_METHOD);
084                // must call this here, because execute in the super method will never have control for this particular action
085                // this is called in the parent by super.execute()
086                this.populateAuthorizationFields(journalVoucherForm);
087            }
088            else { // otherwise call the super
089                returnForward = super.execute(mapping, journalVoucherForm, request, response);
090            }
091            return returnForward;
092        }
093    
094        /**
095         * Overrides the parent to first prompt the user appropriately to make sure that they want to submit and out of balance
096         * document, then calls super's route method.
097         * 
098         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#route(org.apache.struts.action.ActionMapping,
099         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
100         */
101        @Override
102        public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
103            // process the question but we need to make sure there are lines and then check to see if it's not balanced
104            VoucherDocument vDoc = ((VoucherForm) form).getVoucherDocument();
105    
106            KualiDecimal balance = vDoc.getCreditTotal().subtract(vDoc.getDebitTotal());
107            if (vDoc.getSourceAccountingLines().size() > 0 && balance.compareTo(KualiDecimal.ZERO) != 0) {
108                // it's not in "balance"
109                ActionForward returnForward = processRouteOutOfBalanceDocumentConfirmationQuestion(mapping, form, request, response);
110    
111                // if not null, then the question component either has control of the flow and needs to ask its questions
112                // or the person chose the "cancel" or "no" button
113                // otherwise we have control
114                if (returnForward != null) {
115                    return returnForward;
116                }
117            }
118            // now call the route method
119            return super.route(mapping, form, request, response);
120        }
121    
122        /**
123         * This method handles grabbing the values from the form and pushing them into the document appropriately.
124         * 
125         * @param journalVoucherForm
126         */
127        protected void populateBalanceTypeOneDocument(JournalVoucherForm journalVoucherForm) {
128            String selectedBalanceTypeCode = journalVoucherForm.getSelectedBalanceType().getCode();
129            BalanceType selectedBalanceType = getPopulatedBalanceTypeInstance(selectedBalanceTypeCode);
130            journalVoucherForm.getJournalVoucherDocument().setBalanceTypeCode(selectedBalanceTypeCode);
131            journalVoucherForm.getJournalVoucherDocument().setBalanceType(selectedBalanceType); // set the fully populated balance type
132            // object into the form's selected
133            // balance type
134            journalVoucherForm.setSelectedBalanceType(selectedBalanceType);
135        }
136    
137    
138        /**
139         * Overrides to call super, and then to repopulate the credit/debit amounts b/c the credit/debit code might change during a JV
140         * error correction.
141         * 
142         * @see org.kuali.kfs.fp.document.web.struts.VoucherAction#correct(org.apache.struts.action.ActionMapping,
143         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
144         */
145        @Override
146        public ActionForward correct(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
147            ActionForward actionForward = super.correct(mapping, form, request, response);
148    
149            JournalVoucherDocument jvDoc = (JournalVoucherDocument) ((JournalVoucherForm) form).getDocument();
150    
151            jvDoc.refreshReferenceObject(KFSPropertyConstants.BALANCE_TYPE);
152            // only repopulate if this is a JV that was entered in debit/credit mode
153            if (jvDoc.getBalanceType().isFinancialOffsetGenerationIndicator()) {
154                // now make sure to repopulate credit/debit amounts
155                populateAllVoucherAccountingLineHelpers((JournalVoucherForm) form);
156            }
157    
158            return actionForward;
159        }
160    
161        /**
162         * This method processes a change in the balance type for a Journal Voucher document - from either a offset generation balance
163         * type to a non-offset generation balance type or visa-versa.
164         * 
165         * @param mapping
166         * @param form
167         * @param request
168         * @param response
169         * @return ActionForward
170         * @throws Exception
171         */
172        public ActionForward changeBalanceType(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
173            JournalVoucherForm journalVoucherForm = (JournalVoucherForm) form;
174    
175            // figure out which way the balance type is changing
176            int balanceTypeAmountChangeMode = determineBalanceTypeAmountChangeMode(journalVoucherForm);
177            int balanceTypeExternalEncumbranceChangeMode = determineBalanceTypeEncumbranceChangeMode(journalVoucherForm);
178    
179            // process the question
180            if (balanceTypeAmountChangeMode != NO_MODE_CHANGE || balanceTypeExternalEncumbranceChangeMode != NO_MODE_CHANGE) {
181                ActionForward returnForward = processChangeBalanceTypeConfirmationQuestion(mapping, form, request, response);
182    
183                // if not null, then the question component either has control of the flow and needs to ask its questions
184                // or the person choose the "cancel" or "no" button otherwise we have control
185                if (returnForward != null) {
186                    return returnForward;
187                }
188                else {
189                    // deal with balance type changes first amount change
190                    if (balanceTypeAmountChangeMode == CREDIT_DEBIT_TO_SINGLE_AMT_MODE) {
191                        switchFromCreditDebitModeToSingleAmountMode(journalVoucherForm);
192                    }
193                    else if (balanceTypeAmountChangeMode == SINGLE_AMT_TO_CREDIT_DEBIT_MODE) {
194                        switchFromSingleAmountModeToCreditDebitMode(journalVoucherForm);
195                    }
196    
197                    // then look to see if the external encumbrance was involved
198                    if (balanceTypeExternalEncumbranceChangeMode == EXT_ENCUMB_TO_NON_EXT_ENCUMB) {
199                        switchFromExternalEncumbranceModeToNonExternalEncumbrance(journalVoucherForm);
200                    }
201                }
202            }
203    
204            return mapping.findForward(KFSConstants.MAPPING_BASIC);
205        }
206    
207        /**
208         * This method will determine which balance type amount mode to switch to. A change in the balance type selection will
209         * eventually invoke this mechanism, which looks at the old balance type value, and the new balance type value to determine what
210         * the next mode is.
211         * 
212         * @param journalVoucherForm
213         * @throws Exception
214         */
215        protected int determineBalanceTypeAmountChangeMode(JournalVoucherForm journalVoucherForm) throws Exception {
216            int balanceTypeAmountChangeMode = NO_MODE_CHANGE;
217    
218            // retrieve fully populated balance type instances
219            BalanceType origBalType = getPopulatedBalanceTypeInstance(journalVoucherForm.getOriginalBalanceType());
220            BalanceType newBalType = getPopulatedBalanceTypeInstance(journalVoucherForm.getSelectedBalanceType().getCode());
221    
222            // figure out which ways we are switching the modes first deal with amount changes
223            if (origBalType.isFinancialOffsetGenerationIndicator() && !newBalType.isFinancialOffsetGenerationIndicator()) { // credit/debit
224                balanceTypeAmountChangeMode = CREDIT_DEBIT_TO_SINGLE_AMT_MODE;
225            }
226            else if (!origBalType.isFinancialOffsetGenerationIndicator() && newBalType.isFinancialOffsetGenerationIndicator()) { // single
227                balanceTypeAmountChangeMode = SINGLE_AMT_TO_CREDIT_DEBIT_MODE;
228            }
229    
230            return balanceTypeAmountChangeMode;
231        }
232    
233        /**
234         * This method will determine which balance type encumbrance mode to switch to. A change in the balance type selection will
235         * eventually invoke this mechanism, which looks at the old balance type value, and the new balance type value to determine what
236         * the next mode is.
237         * 
238         * @param journalVoucherForm
239         * @throws Exception
240         */
241        protected int determineBalanceTypeEncumbranceChangeMode(JournalVoucherForm journalVoucherForm) throws Exception {
242            int balanceTypeExternalEncumbranceChangeMode = NO_MODE_CHANGE;
243    
244            // retrieve fully populated balance type instances
245            BalanceType origBalType = getPopulatedBalanceTypeInstance(journalVoucherForm.getOriginalBalanceType());
246            BalanceType newBalType = getPopulatedBalanceTypeInstance(journalVoucherForm.getSelectedBalanceType().getCode());
247    
248            // then deal with external encumbrance changes
249            if (origBalType.getCode().equals(KFSConstants.BALANCE_TYPE_EXTERNAL_ENCUMBRANCE) && !newBalType.getCode().equals(KFSConstants.BALANCE_TYPE_EXTERNAL_ENCUMBRANCE)) {
250                balanceTypeExternalEncumbranceChangeMode = EXT_ENCUMB_TO_NON_EXT_ENCUMB;
251            }
252            else if (!origBalType.getCode().equals(KFSConstants.BALANCE_TYPE_EXTERNAL_ENCUMBRANCE) && newBalType.getCode().equals(KFSConstants.BALANCE_TYPE_EXTERNAL_ENCUMBRANCE)) {
253                balanceTypeExternalEncumbranceChangeMode = NON_EXT_ENCUMB_TO_EXT_ENCUMB;
254            }
255    
256            return balanceTypeExternalEncumbranceChangeMode;
257        }
258    
259        /**
260         * This method takes control from the changeBalanceType action method in order to present a question prompt to the user so that
261         * they can confirm the change in balance type.
262         * 
263         * @param mapping
264         * @param form
265         * @param request
266         * @param response
267         * @return ActionForward
268         * @throws Exception
269         */
270        protected ActionForward processChangeBalanceTypeConfirmationQuestion(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
271            JournalVoucherForm jvForm = (JournalVoucherForm) form;
272            JournalVoucherDocument jvDoc = jvForm.getJournalVoucherDocument();
273    
274            // only want to present the confirmation question to the user if there are any
275            // accouting lines, because that is the only impact
276            if (jvDoc.getSourceAccountingLines().size() != 0) {
277                String question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
278                KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class);
279    
280                if (question == null) { // question hasn't been asked
281                    String message = buildBalanceTypeChangeConfirmationMessage(jvForm, kualiConfiguration);
282    
283                    // now transfer control over to the question component
284                    return this.performQuestionWithoutInput(mapping, form, request, response, KFSConstants.JOURNAL_VOUCHER_CHANGE_BALANCE_TYPE_QUESTION, message, KFSConstants.CONFIRMATION_QUESTION, KFSConstants.CHANGE_JOURNAL_VOUCHER_BALANCE_TYPE_METHOD, "");
285                }
286                else {
287                    String buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
288                    if ((KFSConstants.JOURNAL_VOUCHER_CHANGE_BALANCE_TYPE_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) {
289                        // if no button clicked keep the old value and reload doc
290                        BalanceType origBalType = getPopulatedBalanceTypeInstance(jvForm.getOriginalBalanceType());
291                        jvForm.setSelectedBalanceType(origBalType);
292                        jvDoc.setBalanceType(origBalType);
293                        jvDoc.setBalanceTypeCode(origBalType.getCode());
294                        return mapping.findForward(KFSConstants.MAPPING_BASIC);
295                    }
296                }
297            }
298            return null;
299        }
300    
301        /**
302         * This method will setup the message that will get displayed to the user when they are asked to confirm the balance type
303         * change. The message is tuned to the particular context, the value chosen, and also the previous value. It also combines with
304         * the core part of the message which is part of the ApplicationResources.properties file.
305         * 
306         * @param jvForm
307         * @param kualiConfiguration
308         * @return The message to display to the user in the question prompt window.
309         * @throws Exception
310         */
311        protected String buildBalanceTypeChangeConfirmationMessage(JournalVoucherForm jvForm, KualiConfigurationService kualiConfiguration) throws Exception {
312            String message = new String("");
313    
314            // figure out which way the balance type is changing
315            int balanceTypeAmountChangeMode = determineBalanceTypeAmountChangeMode(jvForm);
316            int balanceTypeExternalEncumbranceChangeMode = determineBalanceTypeEncumbranceChangeMode(jvForm);
317    
318            // grab the right message from the ApplicationResources.properties file depending upon the balance type switching mode
319            if (balanceTypeAmountChangeMode == SINGLE_AMT_TO_CREDIT_DEBIT_MODE) {
320                message = kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_CHANGE_JV_BAL_TYPE_FROM_SINGLE_AMT_TO_CREDIT_DEBIT_MODE);
321                // see if we need the extra bit about the external encumbrance
322                String newMessage = new String("");
323                if (balanceTypeExternalEncumbranceChangeMode == NON_EXT_ENCUMB_TO_EXT_ENCUMB) {
324                    newMessage = StringUtils.replace(message, "{3}", kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_CHANGE_JV_BAL_TYPE_FROM_SINGLE_AMT_TO_EXT_ENCUMB_CREDIT_DEBIT_MODE));
325                }
326                else {
327                    newMessage = StringUtils.replace(message, "{3}", "");
328                }
329                message = new String(newMessage);
330            }
331            else if (balanceTypeAmountChangeMode == CREDIT_DEBIT_TO_SINGLE_AMT_MODE) {
332                message = kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_CHANGE_JV_BAL_TYPE_FROM_CREDIT_DEBIT_TO_SINGLE_AMT_MODE);
333                // see if we need the extra bit about the external encumbrance
334                String newMessage = new String("");
335                if (balanceTypeExternalEncumbranceChangeMode == EXT_ENCUMB_TO_NON_EXT_ENCUMB) {
336                    newMessage = StringUtils.replace(message, "{3}", kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_CHANGE_JV_BAL_TYPE_FROM_EXT_ENCUMB_CREDIT_DEBIT_TO_SINGLE_AMT_MODE));
337                }
338                else {
339                    newMessage = StringUtils.replace(message, "{3}", "");
340                }
341                message = new String(newMessage);
342            }
343            else if (balanceTypeExternalEncumbranceChangeMode == EXT_ENCUMB_TO_NON_EXT_ENCUMB) {
344                message = kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_CHANGE_JV_BAL_TYPE_FROM_EXT_ENCUMB_TO_NON_EXT_ENCUMB);
345            }
346            else if (balanceTypeExternalEncumbranceChangeMode == NON_EXT_ENCUMB_TO_EXT_ENCUMB) {
347                message = kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_CHANGE_JV_BAL_TYPE_FROM_NON_EXT_ENCUMB_TO_EXT_ENCUMB);
348            }
349    
350            // retrieve fully populated balance type instances
351            BalanceType origBalType = getPopulatedBalanceTypeInstance(jvForm.getOriginalBalanceType());
352            BalanceType newBalType = getPopulatedBalanceTypeInstance(jvForm.getSelectedBalanceType().getCode());
353    
354            // now complete building of the message
355            String replacement = "\"" + origBalType.getCode() + "-" + origBalType.getName() + "\"";
356            String newMessage = StringUtils.replace(message, "{0}", replacement);
357    
358            replacement = "\"" + newBalType.getCode() + "-" + newBalType.getName() + "\"";
359            String finalMessage = StringUtils.replace(newMessage, "{1}", replacement);
360    
361            return finalMessage;
362        }
363    
364        /**
365         * This method will fully populate a balance type given the passed in code, by calling the business object service that
366         * retrieves the rest of the instances' information.
367         * 
368         * @param balanceTypeCode
369         * @return BalanceTyp
370         */
371        protected BalanceType getPopulatedBalanceTypeInstance(String balanceTypeCode) {
372            // now we have to get the code and the name of the original and new balance types
373            return SpringContext.getBean(BalanceTypeService.class).getBalanceTypeByCode(balanceTypeCode);
374        }
375    
376        /**
377         * This method will clear out the source line values that aren't needed for the "Single Amount" mode.
378         * 
379         * @param journalVoucherForm
380         */
381        protected void switchFromSingleAmountModeToCreditDebitMode(JournalVoucherForm journalVoucherForm) {
382            // going from single amount to credit/debit view so we want to blank out the amount and the extra "reference" fields
383            // that the single amount view uses
384            JournalVoucherDocument jvDoc = (JournalVoucherDocument) journalVoucherForm.getTransactionalDocument();
385            ArrayList sourceLines = (ArrayList) jvDoc.getSourceAccountingLines();
386            ArrayList helperLines = (ArrayList) journalVoucherForm.getVoucherLineHelpers();
387            helperLines.clear(); // reset so we can add in fresh empty ones
388    
389            // make sure that there is enough space in the list
390            helperLines.ensureCapacity(sourceLines.size());
391    
392            for (int i = 0; i < sourceLines.size(); i++) {
393                VoucherSourceAccountingLine sourceLine = (VoucherSourceAccountingLine) sourceLines.get(i);
394                sourceLine.setAmount(KualiDecimal.ZERO);
395                sourceLine.setDebitCreditCode(KFSConstants.GL_DEBIT_CODE); // default to debit
396    
397                helperLines.add(new VoucherAccountingLineHelperBase()); // populate with a fresh new empty object
398            }
399        }
400    
401        /**
402         * This method will clear out the extra "reference" fields that the external encumbrance balance type uses, but will leave the
403         * amounts since we aren't changing the offset generation code stuff.
404         * 
405         * @param journalVoucherForm
406         */
407        protected void switchFromExternalEncumbranceModeToNonExternalEncumbrance(JournalVoucherForm journalVoucherForm) {
408            // going from external encumbrance view to non external encumbrance view, so we want to blank out the extra "reference"
409            // fields
410            JournalVoucherDocument jvDoc = (JournalVoucherDocument) journalVoucherForm.getTransactionalDocument();
411            ArrayList sourceLines = (ArrayList) jvDoc.getSourceAccountingLines();
412    
413            for (int i = 0; i < sourceLines.size(); i++) {
414                VoucherSourceAccountingLine sourceLine = (VoucherSourceAccountingLine) sourceLines.get(i);
415                sourceLine.setReferenceOriginCode(null); // won't be needed in this mode
416                sourceLine.setReferenceNumber(null); // won't be needed in this mode
417                sourceLine.setReferenceTypeCode(null); // won't be needed in this mode
418            }
419        }
420    
421        /**
422         * This method will clear out the source line values that aren't needed for the "Credit/Debit" mode.
423         * 
424         * @param journalVoucherForm
425         */
426        protected void switchFromCreditDebitModeToSingleAmountMode(JournalVoucherForm journalVoucherForm) {
427            // going from credit/debit view to single amount view so we don't need the debit and credit
428            // indicator set any more and we need to blank out the amount values to zero
429            JournalVoucherDocument jvDoc = journalVoucherForm.getJournalVoucherDocument();
430            ArrayList sourceLines = (ArrayList) jvDoc.getSourceAccountingLines();
431            ArrayList helperLines = (ArrayList) journalVoucherForm.getVoucherLineHelpers();
432    
433            KualiDecimal ZERO = new KualiDecimal("0.00");
434            for (int i = 0; i < sourceLines.size(); i++) {
435                VoucherAccountingLineHelper helperLine = (VoucherAccountingLineHelper) helperLines.get(i);
436                SourceAccountingLine sourceLine = (SourceAccountingLine) sourceLines.get(i);
437                sourceLine.setAmount(ZERO);
438                sourceLine.setDebitCreditCode(KFSConstants.GL_DEBIT_CODE); // single sided is always debit
439    
440                helperLine.setCredit(null); // won't be needed in this mode
441                helperLine.setDebit(null); // won't be needed in this mode
442            }
443        }
444    
445        /**
446         * Overrides the parent to make sure that the JV specific accounting line helper forms are properly populated when the document
447         * is first loaded. This first calls super, then populates the helper objects.
448         * 
449         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
450         */
451        @Override
452        protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
453            super.loadDocument(kualiDocumentFormBase);
454            JournalVoucherForm journalVoucherForm = (JournalVoucherForm) kualiDocumentFormBase;
455    
456            // if the balance type is an offset generation balance type, then the user is able to enter the amount
457            // as either a debit or a credit, otherwise, they only need to deal with the amount field
458            JournalVoucherDocument journalVoucherDocument = (JournalVoucherDocument) journalVoucherForm.getTransactionalDocument();
459            if (journalVoucherDocument.getBalanceType().isFinancialOffsetGenerationIndicator()) {
460                populateAllVoucherAccountingLineHelpers(journalVoucherForm);
461                KualiDecimal ZERO = new KualiDecimal("0.00");
462                journalVoucherForm.setNewSourceLineCredit(ZERO);
463                journalVoucherForm.setNewSourceLineDebit(ZERO);
464            }
465    
466            // always wipe out the new source line
467            journalVoucherForm.setNewSourceLine(null);
468    
469            // reload the balance type and accounting period selections since now we have data in the document bo
470            populateSelectedJournalBalanceType(journalVoucherDocument, journalVoucherForm);
471            populateSelectedAccountingPeriod(journalVoucherDocument, journalVoucherForm);
472        }
473    
474        /**
475         * This method grabs the value from the document bo and sets the selected balance type appropriately.
476         * 
477         * @param journalVoucherDocument
478         * @param journalVoucherForm
479         */
480        protected void populateSelectedJournalBalanceType(JournalVoucherDocument journalVoucherDocument, JournalVoucherForm journalVoucherForm) {
481            journalVoucherForm.setSelectedBalanceType(journalVoucherDocument.getBalanceType());
482            if (StringUtils.isNotBlank(journalVoucherDocument.getBalanceTypeCode())) {
483                journalVoucherForm.setOriginalBalanceType(journalVoucherDocument.getBalanceTypeCode());
484            }
485        }
486    
487        /**
488         * This helper method determines from the request object instance whether or not the user has been prompted about the journal
489         * being out of balance. If they haven't, then the method will build the appropriate message given the state of the document and
490         * return control to the question component so that the user receives the "yes"/"no" prompt. If the question has been asked, the
491         * we evaluate the user's answer and direct the flow appropriately. If they answer with a "No", then we build out a message
492         * stating that they chose that value and return an ActionForward of a MAPPING_BASIC which keeps them at the same page that they
493         * were on. If they choose "Yes", then we return a null ActionForward, which the calling action method recognizes as a "Yes" and
494         * continues on processing the "Route."
495         * 
496         * @param mapping
497         * @param form
498         * @param request
499         * @param response
500         * @return ActionForward
501         * @throws Exception
502         */
503        @Override
504        protected ActionForward processRouteOutOfBalanceDocumentConfirmationQuestion(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
505            JournalVoucherForm jvForm = (JournalVoucherForm) form;
506            JournalVoucherDocument jvDoc = jvForm.getJournalVoucherDocument();
507    
508            String question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
509            KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class);
510    
511            if (question == null) { // question hasn't been asked
512                String currencyFormattedDebitTotal = (String) new CurrencyFormatter().format(jvDoc.getDebitTotal());
513                String currencyFormattedCreditTotal = (String) new CurrencyFormatter().format(jvDoc.getCreditTotal());
514                String currencyFormattedTotal = (String) new CurrencyFormatter().format(jvDoc.getTotalDollarAmount());
515                String message = "";
516                jvDoc.refreshReferenceObject(KFSPropertyConstants.BALANCE_TYPE);
517                if (jvDoc.getBalanceType().isFinancialOffsetGenerationIndicator()) {
518                    message = StringUtils.replace(kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_ROUTE_OUT_OF_BALANCE_JV_DOC), "{0}", currencyFormattedDebitTotal);
519                    message = StringUtils.replace(message, "{1}", currencyFormattedCreditTotal);
520                }
521                else {
522                    message = StringUtils.replace(kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_ROUTE_OUT_OF_BALANCE_JV_DOC_SINGLE_AMT_MODE), "{0}", currencyFormattedTotal);
523                }
524    
525                // now transfer control over to the question component
526                return this.performQuestionWithoutInput(mapping, form, request, response, KFSConstants.JOURNAL_VOUCHER_ROUTE_OUT_OF_BALANCE_DOCUMENT_QUESTION, message, KFSConstants.CONFIRMATION_QUESTION, KFSConstants.ROUTE_METHOD, "");
527            }
528            else {
529                String buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
530                if ((KFSConstants.JOURNAL_VOUCHER_ROUTE_OUT_OF_BALANCE_DOCUMENT_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) {
531                    GlobalVariables.getMessageList().add(KFSKeyConstants.MESSAGE_JV_CANCELLED_ROUTE);
532                    return mapping.findForward(KFSConstants.MAPPING_BASIC);
533                }
534            }
535            return null;
536        }
537    
538        /**
539         * This action executes a call to upload CSV accounting line values as SourceAccountingLines for a given transactional document.
540         * The "uploadAccountingLines()" method handles the multi-part request.
541         * 
542         * @param mapping
543         * @param form
544         * @param request
545         * @param response
546         * @return ActionForward
547         * @throws FileNotFoundException
548         * @throws IOException
549         */
550        @Override
551        public ActionForward uploadSourceLines(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException, IOException {
552            // call method that sourceform and destination list
553            uploadAccountingLines(true, form);
554    
555            return mapping.findForward(KFSConstants.MAPPING_BASIC);
556        }
557    
558        /**
559         * This method determines whether we are uploading source or target lines, and then calls uploadAccountingLines directly on the
560         * document object. This method handles retrieving the actual upload file as an input stream into the document.
561         * 
562         * @param isSource
563         * @param form
564         * @throws FileNotFoundException
565         * @throws IOException
566         */
567        @Override
568        protected void uploadAccountingLines(boolean isSource, ActionForm form) throws FileNotFoundException, IOException {
569            JournalVoucherForm jvForm = (JournalVoucherForm) form;
570            // JournalVoucherAccountingLineParser needs a fresh BalanceType BO in the JournalVoucherDocument.
571            jvForm.getJournalVoucherDocument().refreshReferenceObject(KFSPropertyConstants.BALANCE_TYPE);
572            super.uploadAccountingLines(isSource, jvForm);
573        }
574    
575    }