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.module.purap.document.web.struts;
017
018 import java.io.FileNotFoundException;
019 import java.io.IOException;
020 import java.util.Enumeration;
021 import java.util.HashMap;
022 import java.util.Iterator;
023 import java.util.List;
024 import java.util.Map;
025 import java.util.Map.Entry;
026
027 import javax.servlet.http.HttpServletRequest;
028 import javax.servlet.http.HttpServletResponse;
029
030 import org.apache.commons.lang.StringUtils;
031 import org.apache.struts.action.ActionForm;
032 import org.apache.struts.action.ActionForward;
033 import org.apache.struts.action.ActionMapping;
034 import org.apache.struts.upload.FormFile;
035 import org.kuali.kfs.coa.service.AccountService;
036 import org.kuali.kfs.module.purap.PurapConstants;
037 import org.kuali.kfs.module.purap.PurapPropertyConstants;
038 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
039 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLineParser;
040 import org.kuali.kfs.module.purap.businessobject.PurApItem;
041 import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
042 import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase;
043 import org.kuali.kfs.module.purap.document.service.PurapService;
044 import org.kuali.kfs.module.purap.service.PurapAccountingService;
045 import org.kuali.kfs.sys.KFSConstants;
046 import org.kuali.kfs.sys.KFSPropertyConstants;
047 import org.kuali.kfs.sys.businessobject.AccountingLine;
048 import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
049 import org.kuali.kfs.sys.context.SpringContext;
050 import org.kuali.kfs.sys.document.validation.event.AddAccountingLineEvent;
051 import org.kuali.kfs.sys.exception.AccountingLineParserException;
052 import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase;
053 import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentFormBase;
054 import org.kuali.rice.core.util.RiceConstants;
055 import org.kuali.rice.kew.exception.WorkflowException;
056 import org.kuali.rice.kns.service.DocumentService;
057 import org.kuali.rice.kns.service.KualiRuleService;
058 import org.kuali.rice.kns.service.PersistenceService;
059 import org.kuali.rice.kns.util.GlobalVariables;
060 import org.kuali.rice.kns.util.ObjectUtils;
061 import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
062 import org.kuali.rice.kns.web.struts.form.KualiForm;
063 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
064
065 /**
066 * Struts Action for Purchasing and Accounts Payable documents
067 */
068 public class PurchasingAccountsPayableActionBase extends KualiAccountingDocumentActionBase {
069
070 /**
071 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
072 */
073 @Override
074 protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
075 super.loadDocument(kualiDocumentFormBase);
076 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) kualiDocumentFormBase;
077 PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) purapForm.getDocument();
078
079 // refresh the account summary (note this also updates the account amounts)
080 purapForm.refreshAccountSummmary();
081
082 for (org.kuali.rice.kns.bo.Note note : (java.util.List<org.kuali.rice.kns.bo.Note>) document.getDocumentBusinessObject().getBoNotes()) {
083 note.refreshReferenceObject("attachment");
084 }
085
086 // sort the below the line
087 SpringContext.getBean(PurapService.class).sortBelowTheLine(document);
088
089 updateBaseline(document, (PurchasingAccountsPayableFormBase) kualiDocumentFormBase);
090 }
091
092 /**
093 * Updates the baseline accounts on form and doc.
094 *
095 * @param document A descendant of PurchasingAccountsPayableDocument
096 */
097 protected <T extends PurchasingAccountsPayableDocument, V extends KualiAccountingDocumentFormBase> void updateBaseline(T document, V form) {
098 // clear out the old lines first
099 for (PurApItem item : document.getItems()) {
100 // clear out the old lines first
101 item.getBaselineSourceAccountingLines().clear();
102
103 for (PurApAccountingLine sourceAccount : item.getSourceAccountingLines()) {
104 // JHK: KFSMI-287 - removed deep copy since this object will be thrown away after the page renders, we just need a
105 // different path to have them stored on the form
106 // ESPECIALLY since PURAP does not allow lines to be reverted (see calls to setRevertible)
107 item.getBaselineSourceAccountingLines().add(sourceAccount);
108 }
109 }
110 }
111
112 /**
113 * Invokes a service method to refresh the account summary.
114 *
115 * @param mapping An ActionMapping
116 * @param form An ActionForm
117 * @param request The HttpServletRequest
118 * @param response The HttpServletResponse
119 * @throws Exception
120 * @return An ActionForward
121 */
122 public ActionForward refreshAccountSummary(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
123 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form;
124 PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) purapForm.getDocument();
125 SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(document);
126 purapForm.refreshAccountSummmary();
127 return mapping.findForward(KFSConstants.MAPPING_BASIC);
128 }
129
130 /**
131 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#uploadAccountingLines(boolean,org.apache.struts.action.ActionForm)
132 */
133 @Override
134 protected void uploadAccountingLines(boolean isSource, ActionForm form) throws FileNotFoundException, IOException {
135 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form;
136 PurchasingAccountsPayableDocumentBase purapDocument = (PurchasingAccountsPayableDocumentBase)purapForm.getFinancialDocument();
137 PurApAccountingLineParser accountingLineParser = (PurApAccountingLineParser)purapDocument.getAccountingLineParser();
138 List importedLines = null;
139 String errorPathPrefix = PurapConstants.ACCOUNT_DISTRIBUTION_ERROR_KEY;
140 //String errorPathPrefix = "accountDistributionnewSourceLine";
141
142 // import the lines
143 try {
144 FormFile sourceFile = purapForm.getSourceFile();
145 checkUploadFile(sourceFile);
146 GlobalVariables.getMessageMap().clearErrorPath();
147 GlobalVariables.getMessageMap().addToErrorPath(errorPathPrefix);
148 importedLines = accountingLineParser.importSourceAccountingLines(sourceFile.getFileName(), sourceFile.getInputStream(), purapDocument);
149 GlobalVariables.getMessageMap().removeFromErrorPath(errorPathPrefix);
150 }
151 catch (AccountingLineParserException e) {
152 GlobalVariables.getMessageMap().putError(errorPathPrefix, e.getErrorKey(), e.getErrorParameters());
153 }
154
155 // add to list those lines successfully imported
156 if (importedLines != null) {
157 for (Iterator iter = importedLines.iterator(); iter.hasNext();) {
158 PurApAccountingLine importedLine = (PurApAccountingLine) iter.next();
159 //boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPathPrefix, purapForm.getDocument(), (AccountingLine) importedLine));
160 //if (rulePassed) {
161 // add accountingLine
162 SpringContext.getBean(PersistenceService.class).retrieveNonKeyFields(importedLine);
163 ((PurchasingFormBase)purapForm).addAccountDistributionsourceAccountingLine(importedLine);
164 //}
165 }
166 }
167 }
168
169 /**
170 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#insertSourceLine(org.apache.struts.action.ActionMapping,
171 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
172 */
173 @Override
174 public ActionForward insertSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
175 // It would be preferable to find a way to genericize the KualiAccountingDocument methods but this will work for now
176 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form;
177
178 // index of item selected
179 int itemIndex = getSelectedLine(request);
180 PurApItem item = null;
181
182 // if custom processing of an accounting line is not done then insert a line generically.
183 if (processCustomInsertAccountingLine(purapForm, request) == false) {
184 String errorPrefix = null;
185 PurApAccountingLine line = null;
186 boolean rulePassed = false;
187 if (itemIndex >= 0) {
188 item = (PurApItem) ((PurchasingAccountsPayableDocument) purapForm.getDocument()).getItem((itemIndex));
189 line = (PurApAccountingLine) ObjectUtils.deepCopy(item.getNewSourceLine());
190 //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line);
191 errorPrefix = KFSPropertyConstants.DOCUMENT + "." + PurapPropertyConstants.ITEM + "[" + Integer.toString(itemIndex) + "]." + KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME;
192 rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPrefix, purapForm.getDocument(), (AccountingLine) line));
193 }
194 else if (itemIndex == -2){
195 //corrected: itemIndex == -2 is the only case for distribute account
196 //This is the case when we're inserting an accounting line for distribute account.
197 line = ((PurchasingFormBase)purapForm).getAccountDistributionnewSourceLine();
198 //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line);
199 errorPrefix = PurapPropertyConstants.ACCOUNT_DISTRIBUTION_NEW_SRC_LINE;
200 rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPrefix, purapForm.getDocument(), (AccountingLine) line));
201 }
202
203 if (rulePassed) {
204 // add accountingLine
205 SpringContext.getBean(PersistenceService.class).retrieveNonKeyFields(line);
206 if (itemIndex >=0) {
207 insertAccountingLine(purapForm, item, line);
208 // clear the temp account
209 item.resetAccount();
210 }
211 else if (itemIndex == -2) {
212 //this is the case for distribute account
213 ((PurchasingFormBase)purapForm).addAccountDistributionsourceAccountingLine(line);
214 }
215 }
216 }
217
218 return mapping.findForward(KFSConstants.MAPPING_BASIC);
219 }
220
221 /**
222 * Insert the given Accounting Line in several appropriate places in the given item and given form.
223 *
224 * @param financialDocumentForm A form that inherits from PurchasingAccountsPaybleFormBase
225 * @param item A PurApItem
226 * @param line A PurApAccountingLine
227 */
228 protected void insertAccountingLine(PurchasingAccountsPayableFormBase financialDocumentForm, PurApItem item, PurApAccountingLine line) {
229 PurchasingAccountsPayableDocument preq = (PurchasingAccountsPayableDocument) financialDocumentForm.getDocument();
230
231 // add it to the item
232 item.getSourceAccountingLines().add(line);
233 }
234
235 /**
236 * Allows the custom processing of an accounting line during a call to insert source line. If a custom method for inserting an
237 * accounting line was performed, then a value of true must be returned.
238 *
239 * @param purapForm
240 * @param request
241 * @return boolean indicating if validation succeeded
242 */
243 public boolean processCustomInsertAccountingLine(PurchasingAccountsPayableFormBase purapForm, HttpServletRequest request) {
244 return false;
245 }
246
247 /**
248 * Insert the given Accounting Line in several appropriate places in the given item and given form.
249 *
250 * @param financialDocumentForm A form that inherits from KualiAccountingDocumentFormBase
251 * @param item A PurApItem
252 * @param line A PurApAccountingLine
253 */
254 protected void insertAccountingLine(KualiAccountingDocumentFormBase financialDocumentForm, PurApItem item, PurApAccountingLine line) {
255 // add it to the item
256 item.getSourceAccountingLines().add(line);
257 }
258
259 /**
260 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#deleteSourceLine(org.apache.struts.action.ActionMapping,
261 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
262 */
263 @Override
264 public ActionForward deleteSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
265 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form;
266
267 String[] indexes = getSelectedLineForAccounts(request);
268 int itemIndex = Integer.parseInt(indexes[0]);
269 int accountIndex = Integer.parseInt(indexes[1]);
270
271 PurApItem item = (PurApItem) ((PurchasingAccountsPayableDocument) purapForm.getDocument()).getItem((itemIndex));
272 item.getSourceAccountingLines().remove(accountIndex);
273
274 // remove the decorator
275 // financialDocumentForm.getSourceLineDecorators().remove(decorator);
276
277 return mapping.findForward(KFSConstants.MAPPING_BASIC);
278 }
279
280 /**
281 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#getSourceAccountingLine(org.apache.struts.action.ActionForm,
282 * javax.servlet.http.HttpServletRequest)
283 */
284 @Override
285 public SourceAccountingLine getSourceAccountingLine(ActionForm form, HttpServletRequest request) {
286 String[] indexes = getSelectedLineForAccounts(request);
287 int itemIndex = Integer.parseInt(indexes[0]);
288 int accountIndex = Integer.parseInt(indexes[1]);
289 PurchasingAccountsPayableFormBase purchasingAccountsPayableForm = (PurchasingAccountsPayableFormBase) form;
290 SourceAccountingLine line;
291 if (itemIndex == -2) {
292 line = customAccountRetrieval(accountIndex, purchasingAccountsPayableForm);
293 }
294 else {
295 PurApItem item = (PurApItem) ((PurchasingAccountsPayableDocument) purchasingAccountsPayableForm.getDocument()).getItem((itemIndex));
296 line = (SourceAccountingLine) ObjectUtils.deepCopy(item.getSourceAccountingLines().get(accountIndex));
297 }
298 return line;
299 }
300
301 /**
302 * Perform custom processing on accounting lines. See <code>getSelectedLineForAccounts</code>.
303 *
304 * @param accountIndex The index of the account into the request parameter
305 * @param purchasingAccountsPayableForm A form which inherits from PurchasingAccountsPayableFormBase
306 * @return A SourceAccountingLine
307 */
308 protected SourceAccountingLine customAccountRetrieval(int accountIndex, PurchasingAccountsPayableFormBase purchasingAccountsPayableForm) {
309 // default impl returns null
310 return null;
311 }
312
313 /**
314 * Will return an array of Strings containing 2 indexes, the first String is the item index and the second String is the account
315 * index. These are obtained by parsing the method to call parameter from the request, between the word ".line" and "." The
316 * indexes are separated by a semicolon (:)
317 *
318 * @param request The HttpServletRequest
319 * @return An array of Strings containing pairs of two indices, an item index and a account index
320 */
321 protected String[] getSelectedLineForAccounts(HttpServletRequest request) {
322 String accountString = new String();
323 String parameterName = (String) request.getAttribute(KFSConstants.METHOD_TO_CALL_ATTRIBUTE);
324 if (StringUtils.isNotBlank(parameterName)) {
325 accountString = StringUtils.substringBetween(parameterName, ".line", ".");
326 }
327 String[] result = StringUtils.split(accountString, ":");
328
329 return result;
330 }
331
332 /**
333 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#downloadBOAttachment(org.apache.struts.action.ActionMapping,
334 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
335 */
336 @Override
337 public ActionForward downloadBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
338 PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) ((PurchasingAccountsPayableFormBase) form).getDocument();
339
340 for (org.kuali.rice.kns.bo.Note note : (java.util.List<org.kuali.rice.kns.bo.Note>) document.getDocumentBusinessObject().getBoNotes()) {
341 note.refreshReferenceObject("attachment");
342 }
343
344 return super.downloadBOAttachment(mapping, form, request, response);
345 }
346
347 @Override
348 protected void processAccountingLineOverrides(List accountingLines) {
349 //do nothing purap handles these differently
350 }
351
352 /**
353 * Perform calculation on item line.
354 *
355 * @param mapping An ActionMapping
356 * @param form An ActionForm
357 * @param request The HttpServletRequest
358 * @param response The HttpServletResponse
359 * @return An ActionForward
360 */
361 public ActionForward calculate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
362 return mapping.findForward(KFSConstants.MAPPING_BASIC);
363 }
364
365 public ActionForward clearAllTaxes(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
366 return mapping.findForward(KFSConstants.MAPPING_BASIC);
367 }
368
369 protected void customCalculate(PurchasingAccountsPayableDocument purapDoc) {
370 // do nothing by default
371 }
372
373 /**
374 * Toggles all specific tabs to open
375 *
376 * @param mapping
377 * @param form
378 * @param request
379 * @param response
380 * @return
381 * @throws Exception
382 */
383 public ActionForward showAllAccounts(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
384 KualiForm kualiForm = (KualiForm) form;
385 String accountingLineTab = "AccountingLines";
386 String value = null;
387
388 Map<String, String> tabStates = kualiForm.getTabStates();
389 Map<String, String> newTabStates = new HashMap<String, String>();
390 for (Entry<String, String> tabEntry: tabStates.entrySet()) {
391 if(tabEntry.getKey().startsWith(accountingLineTab)){
392 newTabStates.put(tabEntry.getKey(), "OPEN");
393 }else{
394 if (tabEntry.getValue() instanceof String) {
395 value = tabEntry.getValue();
396 }
397 else {
398 //This is the case where the value is an Array of String,
399 //so we'll have to get the first element
400 Object result = tabEntry.getValue();
401 result.getClass();
402 value = ((String[])result)[0];
403 }
404 newTabStates.put(tabEntry.getKey(), value);
405 }
406 }
407 kualiForm.setTabStates(newTabStates);
408 return mapping.findForward(RiceConstants.MAPPING_BASIC);
409 }
410
411 /**
412 * Toggles all specific tabs to closed
413 *
414 * @param mapping
415 * @param form
416 * @param request
417 * @param response
418 * @return
419 * @throws Exception
420 */
421 public ActionForward hideAllAccounts(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
422 KualiForm kualiForm = (KualiForm) form;
423 String accountingLineTab = "AccountingLines";
424 String value = null;
425
426 Map<String, String> tabStates = kualiForm.getTabStates();
427 Map<String, String> newTabStates = new HashMap<String, String>();
428 for (Entry<String, String> tabEntry: tabStates.entrySet()) {
429 if(tabEntry.getKey().startsWith(accountingLineTab)){
430 newTabStates.put(tabEntry.getKey(), "CLOSE");
431 }else{
432 if (tabEntry.getValue() instanceof String) {
433 value = tabEntry.getValue();
434 }
435 else {
436 //This is the case where the value is an Array of String,
437 //so we'll have to get the first element
438 Object result = tabEntry.getValue();
439 result.getClass();
440 value = ((String[])result)[0];
441 }
442 newTabStates.put(tabEntry.getKey(), value);
443 }
444 }
445 kualiForm.setTabStates(newTabStates);
446 return mapping.findForward(RiceConstants.MAPPING_BASIC);
447 }
448
449 /**
450 * Override to verify the document has been saved before the note is inserted. This will assure the correct parent object id is
451 * associated with the note.
452 *
453 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#insertBONote(org.apache.struts.action.ActionMapping,
454 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
455 */
456 @Override
457 public ActionForward insertBONote(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
458 PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) ((PurchasingAccountsPayableFormBase) form).getDocument();
459 KualiWorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
460
461 if (workflowDocument.stateIsInitiated()) {
462 SpringContext.getBean(DocumentService.class).saveDocument(document);
463 }
464
465 return super.insertBONote(mapping, form, request, response);
466 }
467
468 }