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.fp.businessobject.VoucherAccountingLineHelper;
030 import org.kuali.kfs.fp.businessobject.VoucherAccountingLineHelperBase;
031 import org.kuali.kfs.fp.document.VoucherDocument;
032 import org.kuali.kfs.sys.KFSConstants;
033 import org.kuali.kfs.sys.KFSKeyConstants;
034 import org.kuali.kfs.sys.businessobject.AccountingLine;
035 import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
036 import org.kuali.kfs.sys.context.SpringContext;
037 import org.kuali.kfs.sys.document.AmountTotaling;
038 import org.kuali.kfs.sys.service.UniversityDateService;
039 import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase;
040 import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentFormBase;
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 this document
051 * type. Vouchers are unique in that they define several fields that aren't typically used by the other financial transaction
052 * processing eDocs (i.e. external system fields, object type override, credit and debit amounts).
053 */
054 public class VoucherAction extends KualiAccountingDocumentActionBase {
055 // used to determine which way the change balance type action is switching
056 // these are local constants only used within this action class
057 // these should not be used outside of this class
058
059 /**
060 * Overrides to call super, and then to repopulate the credit/debit amounts b/c the credit/debit code might change during a
061 * voucher error correction.
062 *
063 * @see org.kuali.kfs.sys.document.web.struts.FinancialSystemTransactionalDocumentActionBase#correct(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
064 */
065 @Override
066 public ActionForward correct(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
067 ActionForward actionForward = super.correct(mapping, form, request, response);
068
069 VoucherForm vForm = (VoucherForm) form;
070
071 // now make sure to repopulate credit/debit amounts
072 populateAllVoucherAccountingLineHelpers(vForm);
073
074 return actionForward;
075 }
076
077 /**
078 * Overrides parent to first populate the new source line with the correct debit or credit value, then it calls the parent's
079 * implementation.
080 *
081 * @see org.kuali.module.financial.web.struts.action.KualiFinancialDocumentActionBase#insertSourceLine(org.apache.struts.action.ActionMapping,
082 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
083 */
084 @Override
085 public ActionForward insertSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
086 // cast the form to the right pojo
087 VoucherForm voucherForm = (VoucherForm) form;
088
089 // call the super's method
090 ActionForward actionForward = super.insertSourceLine(mapping, form, request, response);
091
092 if (GlobalVariables.getMessageMap().getErrorCount() == 0) {
093 // since no exceptions were thrown, the add succeeded, so we have to re-init the new credit and debit
094 // attributes, and add a new instance of a helperLine to the helperLines list
095 VoucherAccountingLineHelper helperLine = populateNewVoucherAccountingLineHelper(voucherForm);
096 voucherForm.getVoucherLineHelpers().add(helperLine);
097
098 // now reset the debit and credit fields for adds
099 voucherForm.setNewSourceLineDebit(KualiDecimal.ZERO);
100 voucherForm.setNewSourceLineCredit(KualiDecimal.ZERO);
101 }
102
103 return actionForward;
104 }
105
106 /**
107 * Overrides parent to remove the associated helper line also, and then it call the parent's implementation.
108 *
109 * @see org.kuali.module.financial.web.struts.action.KualiFinancialDocumentActionBase#deleteSourceLine(org.apache.struts.action.ActionMapping,
110 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
111 */
112 @Override
113 public ActionForward deleteSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
114 // cast the form to the right pojo
115 VoucherForm voucherForm = (VoucherForm) form;
116
117 // call the super's method
118 ActionForward actionForward = super.deleteSourceLine(mapping, voucherForm, request, response);
119
120 // now remove the associated helper line
121 int index = getLineToDelete(request);
122 if (voucherForm.getVoucherLineHelpers() != null && voucherForm.getVoucherLineHelpers().size() > index) {
123 voucherForm.getVoucherLineHelpers().remove(getLineToDelete(request));
124 }
125
126 return actionForward;
127 }
128
129 /**
130 * Overrides the parent to make sure that the AV specific accounting line helper forms are properly populated when the document
131 * is first loaded. This first calls super, then populates the helper objects.
132 *
133 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
134 */
135 @Override
136 protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
137 super.loadDocument(kualiDocumentFormBase);
138 VoucherForm voucherForm = (VoucherForm) kualiDocumentFormBase;
139
140 populateAllVoucherAccountingLineHelpers(voucherForm);
141 voucherForm.setNewSourceLineCredit(KualiDecimal.ZERO);
142 voucherForm.setNewSourceLineDebit(KualiDecimal.ZERO);
143
144 // always wipe out the new source line
145 voucherForm.setNewSourceLine(null);
146
147 // reload the accounting period selections since now we have data in the document bo
148 populateSelectedAccountingPeriod(voucherForm.getVoucherDocument(), voucherForm);
149 }
150
151 /**
152 * This method parses the accounting period value from the bo and builds the right string to pass to the form object as the
153 * selected value.
154 *
155 * @param voucherDocument
156 * @param voucherForm
157 */
158 protected void populateSelectedAccountingPeriod(VoucherDocument voucherDocument, VoucherForm voucherForm) {
159 if (StringUtils.isNotBlank(voucherDocument.getPostingPeriodCode())) {
160 String selectedAccountingPeriod = voucherDocument.getPostingPeriodCode();
161 if (null != voucherDocument.getPostingYear()) {
162 selectedAccountingPeriod += voucherDocument.getPostingYear().toString();
163 }
164 else {
165 selectedAccountingPeriod += SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear().toString();
166 }
167 voucherForm.setSelectedAccountingPeriod(selectedAccountingPeriod);
168 }
169 }
170
171 /**
172 * This populates a new helperLine instance with the one that was just added so that the new instance can be added to the
173 * helperLines list.
174 *
175 * @param voucherForm
176 * @return VoucherAccountingLineHelper
177 */
178 protected VoucherAccountingLineHelper populateVoucherAccountingLineHelper(VoucherForm voucherForm) {
179 VoucherAccountingLineHelper helperLine = new VoucherAccountingLineHelperBase();
180
181 KualiDecimal debitAmount = voucherForm.getNewSourceLineDebit();
182 if (debitAmount != null && StringUtils.isNotBlank(debitAmount.toString())) {
183 helperLine.setDebit(debitAmount);
184 }
185
186 KualiDecimal creditAmount = voucherForm.getNewSourceLineCredit();
187 if (creditAmount != null && StringUtils.isNotBlank(creditAmount.toString())) {
188 helperLine.setCredit(creditAmount);
189 }
190
191 return helperLine;
192 }
193
194 /**
195 * This method builds the corresponding list of voucher acounting line helper objects so that a user can differentiate between
196 * credit and debit fields. It does this by iterating over each source accounting line (what the voucher uses) looking at the
197 * debit/credit code and then populateingLineHelpers a corresponding helper form instance with the amount in the appropriate
198 * amount field - credit or debit.
199 *
200 * @param voucherForm
201 */
202 protected void populateAllVoucherAccountingLineHelpers(VoucherForm voucherForm) {
203 // make sure the journal voucher accounting line helper form list is populated properly
204 ArrayList voucherLineHelpers = (ArrayList) voucherForm.getVoucherLineHelpers();
205
206 // make sure the helper list is the right size
207 VoucherDocument vDoc = (VoucherDocument) voucherForm.getTransactionalDocument();
208 int size = vDoc.getSourceAccountingLines().size();
209 voucherLineHelpers.ensureCapacity(size);
210
211 // iterate through each source accounting line and initialize the helper form lines appropriately
212 for (int i = 0; i < size; i++) {
213 // get the bo's accounting line at the right index
214 SourceAccountingLine sourceAccountingLine = vDoc.getSourceAccountingLine(i);
215
216 // instantiate a new helper form to use for populating the helper form list
217 VoucherAccountingLineHelper avAcctLineHelperForm = voucherForm.getVoucherLineHelper(i);
218
219 // figure whether we need to set the credit amount or the debit amount
220 if (StringUtils.isNotBlank(sourceAccountingLine.getDebitCreditCode())) {
221 if (sourceAccountingLine.getDebitCreditCode().equals(KFSConstants.GL_DEBIT_CODE)) {
222 avAcctLineHelperForm.setDebit(sourceAccountingLine.getAmount());
223 avAcctLineHelperForm.setCredit(KualiDecimal.ZERO);
224 }
225 else if (sourceAccountingLine.getDebitCreditCode().equals(KFSConstants.GL_CREDIT_CODE)) {
226 avAcctLineHelperForm.setCredit(sourceAccountingLine.getAmount());
227 avAcctLineHelperForm.setDebit(KualiDecimal.ZERO);
228 }
229 }
230 }
231 }
232
233
234 /**
235 * This helper method determines from the request object instance whether or not the user has been prompted about the journal
236 * being out of balance. If they haven't, then the method will build the appropriate message given the state of the document and
237 * return control to the question component so that the user receives the "yes"/"no" prompt. If the question has been asked, the
238 * we evaluate the user's answer and direct the flow appropriately. If they answer with a "No", then we build out a message
239 * stating that they chose that value and return an ActionForward of a MAPPING_BASIC which keeps them at the same page that they
240 * were on. If they choose "Yes", then we return a null ActionForward, which the calling action method recognizes as a "Yes" and
241 * continues on processing the "Route."
242 *
243 * @param mapping
244 * @param form
245 * @param request
246 * @param response
247 * @return ActionForward
248 * @throws Exception
249 */
250 protected ActionForward processRouteOutOfBalanceDocumentConfirmationQuestion(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
251 VoucherForm vForm = (VoucherForm) form;
252 VoucherDocument avDoc = vForm.getVoucherDocument();
253
254 String question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
255 KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class);
256
257 if (question == null) { // question hasn't been asked
258 String currencyFormattedDebitTotal = (String) new CurrencyFormatter().format(avDoc.getDebitTotal());
259 String currencyFormattedCreditTotal = (String) new CurrencyFormatter().format(avDoc.getCreditTotal());
260 String currencyFormattedTotal = (String) new CurrencyFormatter().format(((AmountTotaling) avDoc).getTotalDollarAmount());
261 String message = "";
262 message = StringUtils.replace(kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_ROUTE_OUT_OF_BALANCE_JV_DOC), "{0}", currencyFormattedDebitTotal);
263 message = StringUtils.replace(message, "{1}", currencyFormattedCreditTotal);
264
265 // now transfer control over to the question component
266 return this.performQuestionWithoutInput(mapping, form, request, response, KFSConstants.JOURNAL_VOUCHER_ROUTE_OUT_OF_BALANCE_DOCUMENT_QUESTION, message, KFSConstants.CONFIRMATION_QUESTION, KFSConstants.ROUTE_METHOD, "");
267 }
268 else {
269 String buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
270 if ((KFSConstants.JOURNAL_VOUCHER_ROUTE_OUT_OF_BALANCE_DOCUMENT_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) {
271 GlobalVariables.getMessageList().add(KFSKeyConstants.MESSAGE_JV_CANCELLED_ROUTE);
272 return mapping.findForward(KFSConstants.MAPPING_BASIC);
273 }
274 }
275 return null;
276 }
277
278 /**
279 * This populates a new helperLine instance with the one that was just added so that the new instance can be added to the
280 * helperLines list.
281 *
282 * @param voucherForm
283 * @return voucherAccountingLineHelper
284 */
285 protected VoucherAccountingLineHelper populateNewVoucherAccountingLineHelper(VoucherForm voucherForm) {
286 VoucherAccountingLineHelper helperLine = new VoucherAccountingLineHelperBase();
287
288 KualiDecimal debitAmount = voucherForm.getNewSourceLineDebit();
289 if (debitAmount != null && StringUtils.isNotBlank(debitAmount.toString())) {
290 helperLine.setDebit(debitAmount);
291 }
292
293 KualiDecimal creditAmount = voucherForm.getNewSourceLineCredit();
294 if (creditAmount != null && StringUtils.isNotBlank(creditAmount.toString())) {
295 helperLine.setCredit(creditAmount);
296 }
297
298 return helperLine;
299 }
300
301 /**
302 * This action executes a call to upload CSV accounting line values as SourceAccountingLines for a given transactional document.
303 * The "uploadAccountingLines()" method handles the multi-part request.
304 *
305 * @param mapping
306 * @param form
307 * @param request
308 * @param response
309 * @return ActionForward
310 * @throws FileNotFoundException
311 * @throws IOException
312 */
313 @Override
314 public ActionForward uploadSourceLines(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException, IOException {
315 // call method that sourceform and destination list
316 uploadAccountingLines(true, form);
317
318 return mapping.findForward(KFSConstants.MAPPING_BASIC);
319 }
320
321 /**
322 * This method determines whether we are uploading source or target lines, and then calls uploadAccountingLines directly on the
323 * document object. This method handles retrieving the actual upload file as an input stream into the document.
324 *
325 * @param isSource
326 * @param form
327 * @throws FileNotFoundException
328 * @throws IOException
329 */
330 @Override
331 protected void uploadAccountingLines(boolean isSource, ActionForm form) throws FileNotFoundException, IOException {
332 super.uploadAccountingLines(isSource, form);
333
334 populateAllVoucherAccountingLineHelpers((VoucherForm) form);
335 }
336
337 /**
338 * Overridden to reset the available and selected accounting periods on the form, so that copies are moved forward to the current accounting period correctly
339 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#copy(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
340 */
341 @Override
342 public ActionForward copy(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
343 ActionForward forward = super.copy(mapping, form, request, response);
344 VoucherForm voucherForm = (VoucherForm)form;
345 voucherForm.populateAccountingPeriodListForRendering();
346 voucherForm.populateDefaultSelectedAccountingPeriod();
347 return forward;
348 }
349 }