View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.module.purap.document.web.struts;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.struts.action.ActionForm;
20  import org.apache.struts.action.ActionForward;
21  import org.apache.struts.action.ActionMapping;
22  import org.kuali.ole.module.purap.PurapConstants;
23  import org.kuali.ole.module.purap.PurapConstants.InvoiceStatuses;
24  import org.kuali.ole.module.purap.PurapConstants.PRQSDocumentsStrings;
25  import org.kuali.ole.module.purap.PurapKeyConstants;
26  import org.kuali.ole.module.purap.PurapPropertyConstants;
27  import org.kuali.ole.module.purap.document.AccountsPayableDocument;
28  import org.kuali.ole.module.purap.document.InvoiceDocument;
29  import org.kuali.ole.module.purap.document.PurchaseOrderDocument;
30  import org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument;
31  import org.kuali.ole.module.purap.document.service.InvoiceService;
32  import org.kuali.ole.module.purap.document.service.PurapService;
33  import org.kuali.ole.module.purap.document.service.PurchaseOrderService;
34  import org.kuali.ole.module.purap.document.validation.event.AttributedCalculateAccountsPayableEvent;
35  import org.kuali.ole.module.purap.document.validation.event.AttributedContinuePurapEvent;
36  import org.kuali.ole.module.purap.document.validation.event.AttributedPreCalculateAccountsPayableEvent;
37  import org.kuali.ole.module.purap.service.PurapAccountingService;
38  import org.kuali.ole.module.purap.util.PurQuestionCallback;
39  import org.kuali.ole.sys.OLEConstants;
40  import org.kuali.ole.sys.OLEKeyConstants;
41  import org.kuali.ole.sys.OLEPropertyConstants;
42  import org.kuali.ole.sys.context.SpringContext;
43  import org.kuali.ole.sys.service.UniversityDateService;
44  import org.kuali.rice.core.api.config.property.ConfigurationService;
45  import org.kuali.rice.core.api.util.RiceConstants;
46  import org.kuali.rice.kew.api.exception.WorkflowException;
47  import org.kuali.rice.kim.api.KimConstants;
48  import org.kuali.rice.kns.question.ConfirmationQuestion;
49  import org.kuali.rice.kns.service.DocumentHelperService;
50  import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
51  import org.kuali.rice.krad.service.KualiRuleService;
52  import org.kuali.rice.krad.util.GlobalVariables;
53  import org.kuali.rice.krad.util.KRADConstants;
54  import org.kuali.rice.krad.util.ObjectUtils;
55  
56  import javax.servlet.http.HttpServletRequest;
57  import javax.servlet.http.HttpServletResponse;
58  import java.util.HashMap;
59  
60  /**
61   * Struts Action for Payment Request document.
62   */
63  public class InvoiceAction extends AccountsPayableActionBase {
64      static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(InvoiceAction.class);
65  
66      /**
67       * Do initialization for a new payment request.
68       *
69       * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#createDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
70       */
71      @Override
72      protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
73          super.createDocument(kualiDocumentFormBase);
74          ((InvoiceDocument) kualiDocumentFormBase.getDocument()).initiateDocument();
75      }
76  
77      /**
78       * @see org.kuali.rice.kns.web.struts.action.KualiAction#refresh(org.apache.struts.action.ActionMapping,
79       *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
80       */
81      @Override
82      public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
83          InvoiceForm prqsForm = (InvoiceForm) form;
84          InvoiceDocument document = (InvoiceDocument) prqsForm.getDocument();
85  
86          return super.refresh(mapping, form, request, response);
87      }
88  
89      /**
90       * Executes the continue action on a payment request. Populates and initializes the rest of the payment request besides what was
91       * shown on the init screen.
92       *
93       * @param mapping  An ActionMapping
94       * @param form     An ActionForm
95       * @param request  The HttpServletRequest
96       * @param response The HttpServletResponse
97       * @return An ActionForward
98       * @throws Exception
99       */
100     public ActionForward continuePRQS(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
101         LOG.debug("continuePRQS() method");
102         InvoiceForm prqsForm = (InvoiceForm) form;
103         InvoiceDocument invoiceDocument = (InvoiceDocument) prqsForm.getDocument();
104 
105         boolean poNotNull = true;
106 
107         boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedContinuePurapEvent(invoiceDocument));
108         if (!rulePassed) {
109             return mapping.findForward(OLEConstants.MAPPING_BASIC);
110         }
111 
112         GlobalVariables.getMessageMap().clearErrorPath();
113         GlobalVariables.getMessageMap().addToErrorPath(OLEPropertyConstants.DOCUMENT);
114 
115         //check for a po id
116         if (ObjectUtils.isNull(invoiceDocument.getPurchaseOrderIdentifier())) {
117             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, OLEKeyConstants.ERROR_REQUIRED, PRQSDocumentsStrings.PURCHASE_ORDER_ID);
118             poNotNull = false;
119         }
120 
121         if (ObjectUtils.isNull(invoiceDocument.getInvoiceDate())) {
122             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.INVOICE_DATE, OLEKeyConstants.ERROR_REQUIRED, PRQSDocumentsStrings.INVOICE_DATE);
123             poNotNull = false;
124         }
125 
126         /*if (ObjectUtils.isNull(invoiceDocument.getInvoiceNumber())) {
127             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.INVOICE_NUMBER, OLEKeyConstants.ERROR_REQUIRED, PRQSDocumentsStrings.INVOICE_NUMBER);
128             poNotNull = false;
129         }*/
130         invoiceDocument.setInvoiceNumber(invoiceDocument.getInvoiceNumber().toUpperCase());
131 
132         if (ObjectUtils.isNull(invoiceDocument.getVendorInvoiceAmount())) {
133             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.VENDOR_INVOICE_AMOUNT, OLEKeyConstants.ERROR_REQUIRED, PRQSDocumentsStrings.VENDOR_INVOICE_AMOUNT);
134             poNotNull = false;
135         }
136 
137         //exit early as the po is null, no need to proceed further until this is taken care of
138         if (poNotNull == false) {
139             return mapping.findForward(OLEConstants.MAPPING_BASIC);
140         }
141 
142 
143         PurchaseOrderDocument po = SpringContext.getBean(PurchaseOrderService.class).getCurrentPurchaseOrder(invoiceDocument.getPurchaseOrderIdentifier());
144         if (ObjectUtils.isNotNull(po)) {
145             // TODO figure out a more straightforward way to do this.  ailish put this in so the link id would be set and the perm check would work
146             invoiceDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(po.getAccountsPayablePurchasingDocumentLinkIdentifier());
147 
148             //check to see if user is allowed to initiate doc based on PO sensitive data
149             if (!SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(invoiceDocument).isAuthorizedByTemplate(invoiceDocument, KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.OPEN_DOCUMENT, GlobalVariables.getUserSession().getPrincipalId())) {
150                 throw buildAuthorizationException("initiate document", invoiceDocument);
151             }
152         }
153 
154         if (!SpringContext.getBean(InvoiceService.class).isPurchaseOrderValidForInvoiceDocumentCreation(invoiceDocument, po)) {
155             return mapping.findForward(OLEConstants.MAPPING_BASIC);
156         }
157 
158         // perform duplicate check which will forward to a question prompt if one is found
159         ActionForward forward = performDuplicateInvoiceAndEncumberFiscalYearCheck(mapping, form, request, response, invoiceDocument);
160         if (forward != null) {
161             return forward;
162         }
163 
164         // If we are here either there was no duplicate or there was a duplicate and the user hits continue, in either case we need
165         // to validate the business rules
166         SpringContext.getBean(InvoiceService.class).populateAndSaveInvoice(invoiceDocument);
167 
168         // force calculation
169         prqsForm.setCalculated(false);
170 
171         //TODO if better, move this to the action just before prqs goes into ATAX status
172         // force calculation for tax
173         prqsForm.setCalculatedTax(false);
174 
175         // sort below the line
176         SpringContext.getBean(PurapService.class).sortBelowTheLine(invoiceDocument);
177 
178         // update the counts on the form
179         prqsForm.updateItemCounts();
180 
181         return mapping.findForward(OLEConstants.MAPPING_BASIC);
182     }
183 
184 
185     /**
186      * Clears the initial fields on the <code>InvoiceDocument</code> which should be accessible from the given form.
187      *
188      * @param mapping  An ActionMapping
189      * @param form     An ActionForm, which must be a InvoiceForm
190      * @param request  The HttpServletRequest
191      * @param response The HttpServletResponse
192      * @return An ActionForward
193      * @throws Exception
194      */
195     public ActionForward clearInitFields(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
196         LOG.debug("clearInitValues() method");
197         InvoiceForm prqsForm = (InvoiceForm) form;
198         InvoiceDocument invoiceDocument = (InvoiceDocument) prqsForm.getDocument();
199         invoiceDocument.clearInitFields();
200 
201         return super.refresh(mapping, form, request, response);
202     }
203 
204     /**
205      * This method runs two checks based on the user input on PRQS initiate screen: Encumber next fiscal year check and Duplicate
206      * payment request check. Encumber next fiscal year is checked first and will display a warning message to the user if it's the
207      * case. Duplicate payment request check calls <code>InvoiceService</code> to perform the duplicate payment request
208      * check. If one is found, a question is setup and control is forwarded to the question action method. Coming back from the
209      * question prompt the button that was clicked is checked and if 'no' was selected they are forward back to the page still in
210      * init mode.
211      *
212      * @param mapping         An ActionMapping
213      * @param form            An ActionForm
214      * @param request         The HttpServletRequest
215      * @param response        The HttpServletResponse
216      * @param invoiceDocument The InvoiceDocument
217      * @return An ActionForward
218      * @throws Exception
219      * @see org.kuali.ole.module.purap.document.service.InvoiceService
220      */
221     protected ActionForward performDuplicateInvoiceAndEncumberFiscalYearCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, InvoiceDocument invoiceDocument) throws Exception {
222         ActionForward forward = null;
223         Object question = request.getParameter(OLEConstants.QUESTION_INST_ATTRIBUTE_NAME);
224         if (question == null) {
225             // perform encumber next fiscal year check and prompt warning message if needs
226             if (isEncumberNextFiscalYear(invoiceDocument)) {
227                 String questionText = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(PurapKeyConstants.WARNING_ENCUMBER_NEXT_FY);
228                 return this.performQuestionWithoutInput(mapping, form, request, response, PRQSDocumentsStrings.ENCUMBER_NEXT_FISCAL_YEAR_QUESTION, questionText, OLEConstants.CONFIRMATION_QUESTION, OLEConstants.ROUTE_METHOD, "");
229             } else {
230                 // perform duplicate payment request check
231                 HashMap<String, String> duplicateMessages = SpringContext.getBean(InvoiceService.class).invoiceDuplicateMessages(invoiceDocument);
232                 if (!duplicateMessages.isEmpty()) {
233                     return this.performQuestionWithoutInput(mapping, form, request, response, PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, duplicateMessages.get(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION), OLEConstants.CONFIRMATION_QUESTION, OLEConstants.ROUTE_METHOD, "");
234                 }
235             }
236         } else {
237             Object buttonClicked = request.getParameter(OLEConstants.QUESTION_CLICKED_BUTTON);
238             // If the user replies 'Yes' to the encumber-next-year-question, proceed with duplicate payment check
239             if (PRQSDocumentsStrings.ENCUMBER_NEXT_FISCAL_YEAR_QUESTION.equals(question) && ConfirmationQuestion.YES.equals(buttonClicked)) {
240                 HashMap<String, String> duplicateMessages = SpringContext.getBean(InvoiceService.class).invoiceDuplicateMessages(invoiceDocument);
241                 if (!duplicateMessages.isEmpty()) {
242                     return this.performQuestionWithoutInput(mapping, form, request, response, PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION, duplicateMessages.get(PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION), OLEConstants.CONFIRMATION_QUESTION, OLEConstants.ROUTE_METHOD, "");
243                 }
244             }
245             // If the user replies 'No' to either of the questions, redirect to the PRQS initiate page.
246             else if ((PRQSDocumentsStrings.ENCUMBER_NEXT_FISCAL_YEAR_QUESTION.equals(question) || PRQSDocumentsStrings.DUPLICATE_INVOICE_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) {
247                 invoiceDocument.updateAndSaveAppDocStatus(InvoiceStatuses.APPDOC_INITIATE);
248                 forward = mapping.findForward(OLEConstants.MAPPING_BASIC);
249             }
250         }
251 
252         return forward;
253     }
254 
255     /**
256      * Check if the current PRQS encumber next fiscal year from PO document.
257      *
258      * @param invoiceDocument
259      * @return
260      */
261     protected boolean isEncumberNextFiscalYear(InvoiceDocument invoiceDocument) {
262         Integer fiscalYear = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
263         if (invoiceDocument.getPurchaseOrderDocument().getPostingYear().intValue() > fiscalYear) {
264             return true;
265         }
266         return false;
267     }
268 
269     /**
270      * Puts a payment on hold, prompting for a reason beforehand. This stops further approvals or routing.
271      *
272      * @param mapping  An ActionMapping
273      * @param form     An ActionForm
274      * @param request  The HttpServletRequest
275      * @param response The HttpServletResponse
276      * @return An ActionForward
277      * @throws Exception
278      */
279     public ActionForward addHoldOnPayment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
280         String operation = "Hold ";
281 
282         PurQuestionCallback callback = new PurQuestionCallback() {
283             @Override
284             public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
285                 document = SpringContext.getBean(InvoiceService.class).addHoldOnInvoice((InvoiceDocument) document, noteText);
286                 return document;
287             }
288         };
289 
290         return askQuestionWithInput(mapping, form, request, response, PRQSDocumentsStrings.HOLD_PRQS_QUESTION, PRQSDocumentsStrings.HOLD_NOTE_PREFIX, operation, PurapKeyConstants.PAYMENT_REQUEST_MESSAGE_HOLD_DOCUMENT, callback);
291     }
292 
293     /**
294      * Removes a hold on the payment request.
295      *
296      * @param mapping  An ActionMapping
297      * @param form     An ActionForm
298      * @param request  The HttpServletRequest
299      * @param response The HttpServletResponse
300      * @return An ActionForward
301      * @throws Exception
302      */
303     public ActionForward removeHoldFromPayment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
304         String operation = "Remove ";
305 
306         PurQuestionCallback callback = new PurQuestionCallback() {
307             @Override
308             public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
309                 document = SpringContext.getBean(InvoiceService.class).removeHoldOnInvoice((InvoiceDocument) document, noteText);
310                 return document;
311             }
312         };
313 
314         return askQuestionWithInput(mapping, form, request, response, PRQSDocumentsStrings.REMOVE_HOLD_PRQS_QUESTION, PRQSDocumentsStrings.REMOVE_HOLD_NOTE_PREFIX, operation, PurapKeyConstants.PAYMENT_REQUEST_MESSAGE_REMOVE_HOLD_DOCUMENT, callback);
315     }
316 
317     /**
318      * This action requests a cancel on a prqs, prompting for a reason before hand. This stops further approvals or routing.
319      *
320      * @param mapping  An ActionMapping
321      * @param form     An ActionForm
322      * @param request  The HttpServletRequest
323      * @param response The HttpServletResponse
324      * @return An ActionForward
325      * @throws Exception
326      */
327     public ActionForward requestCancelOnPayment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
328         String operation = "Cancel ";
329 
330         PurQuestionCallback callback = new PurQuestionCallback() {
331             @Override
332             public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
333                 SpringContext.getBean(InvoiceService.class).requestCancelOnInvoice((InvoiceDocument) document, noteText);
334                 return document;
335             }
336         };
337 
338         return askQuestionWithInput(mapping, form, request, response, PRQSDocumentsStrings.CANCEL_PRQS_QUESTION, PRQSDocumentsStrings.CANCEL_NOTE_PREFIX, operation, PurapKeyConstants.PAYMENT_REQUEST_MESSAGE_CANCEL_DOCUMENT, callback);
339     }
340 
341     /**
342      * @see AccountsPayableActionBase#cancelPOActionCallbackMethod()
343      */
344 //    @Override
345 //    protected PurQuestionCallback cancelPOActionCallbackMethod() {
346 //
347 //        return new PurQuestionCallback() {
348 //            public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
349 //                InvoiceDocument prqsDocument = (InvoiceDocument) document;
350 //                prqsDocument.setReopenPurchaseOrderIndicator(true);
351 //                return prqsDocument;
352 //            }
353 //        };
354 //    }
355 
356     /**
357      * Removes a request for cancel on a payment request.
358      *
359      * @param mapping  An ActionMapping
360      * @param form     An ActionForm
361      * @param request  The HttpServletRequest
362      * @param response The HttpServletResponse
363      * @return An ActionForward
364      * @throws Exception
365      */
366     public ActionForward removeCancelRequestFromPayment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
367         String operation = "Cancel ";
368 
369         PurQuestionCallback callback = new PurQuestionCallback() {
370             @Override
371             public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
372                 SpringContext.getBean(InvoiceService.class).removeRequestCancelOnInvoice((InvoiceDocument) document, noteText);
373                 return document;
374             }
375         };
376 
377         return askQuestionWithInput(mapping, form, request, response, PRQSDocumentsStrings.REMOVE_CANCEL_PRQS_QUESTION, PRQSDocumentsStrings.REMOVE_CANCEL_NOTE_PREFIX, operation, PurapKeyConstants.PAYMENT_REQUEST_MESSAGE_REMOVE_CANCEL_DOCUMENT, callback);
378     }
379 
380     /**
381      * Calls a service method to calculate for a payment request document.
382      *
383      * @param apDoc The AccountsPayableDocument
384      */
385     @Override
386     protected void customCalculate(PurchasingAccountsPayableDocument apDoc) {
387         InvoiceDocument prqsDoc = (InvoiceDocument) apDoc;
388 
389         // set amounts on any empty
390         prqsDoc.updateExtendedPriceOnItems();
391 
392         // calculation just for the tax area, only at tax review stage
393         // by now, the general calculation shall have been done.
394         if (StringUtils.equals(prqsDoc.getApplicationDocumentStatus(), InvoiceStatuses.APPDOC_AWAITING_TAX_REVIEW)) {
395             SpringContext.getBean(InvoiceService.class).calculateTaxArea(prqsDoc);
396             return;
397         }
398 
399         // notice we're ignoring whether the boolean, because these are just warnings they shouldn't halt anything
400         //Calculate Payment request before rules since the rule check totalAmount.
401         SpringContext.getBean(InvoiceService.class).calculateInvoice(prqsDoc, true);
402         SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedCalculateAccountsPayableEvent(prqsDoc));
403     }
404 
405     /**
406      * @see AccountsPayableActionBase#getActionName()
407      */
408     @Override
409     public String getActionName() {
410         return PurapConstants.INVOICE_ACTION_NAME;
411     }
412 
413     public ActionForward useAlternateVendor(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
414         InvoiceForm prqsForm = (InvoiceForm) form;
415         InvoiceDocument document = (InvoiceDocument) prqsForm.getDocument();
416 
417         SpringContext.getBean(InvoiceService.class).changeVendor(
418                 document, document.getAlternateVendorHeaderGeneratedIdentifier(), document.getAlternateVendorDetailAssignedIdentifier());
419 
420         return mapping.findForward(OLEConstants.MAPPING_BASIC);
421     }
422 
423     public ActionForward useOriginalVendor(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
424         InvoiceForm prqsForm = (InvoiceForm) form;
425         InvoiceDocument document = (InvoiceDocument) prqsForm.getDocument();
426 
427         SpringContext.getBean(InvoiceService.class).changeVendor(
428                 document, document.getOriginalVendorHeaderGeneratedIdentifier(), document.getOriginalVendorDetailAssignedIdentifier());
429 
430         return mapping.findForward(OLEConstants.MAPPING_BASIC);
431     }
432 
433     @Override
434     public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
435         InvoiceDocument prqs = ((InvoiceForm) form).getInvoiceDocument();
436         SpringContext.getBean(PurapService.class).prorateForTradeInAndFullOrderDiscount(prqs);
437         SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(prqs);
438         if (prqs.isClosePurchaseOrderIndicator()) {
439             PurchaseOrderDocument po = prqs.getPurchaseOrderDocument();
440             if (po.canClosePOForTradeIn()) {
441                 return super.route(mapping, form, request, response);
442             } else {
443                 return mapping.findForward(OLEConstants.MAPPING_BASIC);
444             }
445         } else {
446             return super.route(mapping, form, request, response);
447         }
448     }
449 
450     /**
451      * Overrides to invoke the updateAccountAmounts so that the account percentage will be
452      * correctly updated before validation for account percent is called.
453      *
454      * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#approve(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
455      */
456     @Override
457     public ActionForward approve(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
458         InvoiceDocument prqs = ((InvoiceForm) form).getInvoiceDocument();
459 
460         SpringContext.getBean(PurapService.class).prorateForTradeInAndFullOrderDiscount(prqs);
461         // if tax is required but not yet calculated, return and prompt user to calculate
462         if (requiresCalculateTax((InvoiceForm) form)) {
463             GlobalVariables.getMessageMap().putError(OLEConstants.DOCUMENT_ERRORS, PurapKeyConstants.ERROR_APPROVE_REQUIRES_CALCULATE);
464             return mapping.findForward(OLEConstants.MAPPING_BASIC);
465         }
466 
467         // enforce calculating tax again upon approval, just in case user changes tax data without calculation
468         // other wise there will be a loophole, because the taxCalculated indicator is already set upon first calculation
469         // and thus system wouldn't know it's not re-calculated after tax data are changed
470         if (SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedPreCalculateAccountsPayableEvent(prqs))) {
471             // pre-calculation rules succeed, calculate tax again and go ahead with approval
472             customCalculate(prqs);
473             SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(prqs);
474             return super.approve(mapping, form, request, response);
475         } else {
476             // pre-calculation rules fail, go back to same page with error messages
477             return mapping.findForward(OLEConstants.MAPPING_BASIC);
478         }
479     }
480 
481     /**
482      * Checks if tax calculation is required.
483      * Currently it is required when prqs is awaiting for tax approval and tax has not already been calculated.
484      *
485      * @param invoiceForm A Form, which must inherit from <code>AccountsPayableFormBase</code>
486      * @return true if calculation is required, false otherwise
487      */
488     protected boolean requiresCalculateTax(InvoiceForm invoiceForm) {
489         InvoiceDocument prqs = (InvoiceDocument) invoiceForm.getDocument();
490         boolean requiresCalculateTax = StringUtils.equals(prqs.getApplicationDocumentStatus(), InvoiceStatuses.APPDOC_AWAITING_TAX_REVIEW) && !invoiceForm.isCalculatedTax();
491         return requiresCalculateTax;
492     }
493 
494     public ActionForward changeUseTaxIndicator(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
495         PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) ((PurchasingAccountsPayableFormBase) form).getDocument();
496 
497         //clear/recalculate tax and recreate GL entries
498         SpringContext.getBean(PurapService.class).updateUseTaxIndicator(document, !document.isUseTaxIndicator());
499         SpringContext.getBean(PurapService.class).calculateTax(document);
500 
501         //TODO: add recalculate GL entries hook here
502 
503         return mapping.findForward(OLEConstants.MAPPING_BASIC);
504     }
505 
506     /**
507      * Calls service to clear tax info.
508      */
509     public ActionForward clearTaxInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
510         InvoiceForm prForm = (InvoiceForm) form;
511         InvoiceDocument document = (InvoiceDocument) prForm.getDocument();
512 
513         InvoiceService taxService = SpringContext.getBean(InvoiceService.class);
514 
515         /* call service to clear previous lines */
516         taxService.clearTax(document);
517 
518         return mapping.findForward(OLEConstants.MAPPING_BASIC);
519     }
520 
521     //MSU Contribution OLEMI-8558 DTT-3765 OLECNTRB-963
522     @Override
523     public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
524         InvoiceForm prqsForm = (InvoiceForm) form;
525 
526         InvoiceDocument prqsDocument = (InvoiceDocument) prqsForm.getDocument();
527 
528         ActionForward forward = mapping.findForward(RiceConstants.MAPPING_BASIC);
529         if (prqsDocument.getPurchaseOrderDocument().isPendingActionIndicator()) {
530             GlobalVariables.getMessageMap().putError(
531                     OLEPropertyConstants.DOCUMENT + "." + OLEPropertyConstants.DOCUMENT_NUMBER,
532                     PurapKeyConstants.ERROR_PAYMENT_REQUEST_CANNOT_BE_CANCELLED);
533         } else {
534             forward = super.cancel(mapping, form, request, response);
535         }
536 
537         return forward;
538     }
539 }