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 }