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.util.Iterator;
019    import java.util.Properties;
020    import java.util.TreeMap;
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.module.purap.PurapConstants;
030    import org.kuali.kfs.module.purap.PurapKeyConstants;
031    import org.kuali.kfs.module.purap.PurapPropertyConstants;
032    import org.kuali.kfs.module.purap.SingleConfirmationQuestion;
033    import org.kuali.kfs.module.purap.PurapConstants.AccountsPayableDocumentStrings;
034    import org.kuali.kfs.module.purap.PurapConstants.CMDocumentsStrings;
035    import org.kuali.kfs.module.purap.PurapConstants.PODocumentsStrings;
036    import org.kuali.kfs.module.purap.PurapConstants.PaymentRequestStatuses;
037    import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderDocTypes;
038    import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderStatuses;
039    import org.kuali.kfs.module.purap.document.AccountsPayableDocument;
040    import org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase;
041    import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
042    import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
043    import org.kuali.kfs.module.purap.document.service.AccountsPayableService;
044    import org.kuali.kfs.module.purap.document.service.LogicContainer;
045    import org.kuali.kfs.module.purap.document.service.PurapService;
046    import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
047    import org.kuali.kfs.module.purap.document.validation.event.AttributedPreCalculateAccountsPayableEvent;
048    import org.kuali.kfs.module.purap.util.PurQuestionCallback;
049    import org.kuali.kfs.sys.KFSConstants;
050    import org.kuali.kfs.sys.KFSPropertyConstants;
051    import org.kuali.kfs.sys.context.SpringContext;
052    import org.kuali.kfs.vnd.VendorConstants;
053    import org.kuali.kfs.vnd.businessobject.VendorAddress;
054    import org.kuali.rice.kew.exception.WorkflowException;
055    import org.kuali.rice.kns.bo.Note;
056    import org.kuali.rice.kns.exception.ValidationException;
057    import org.kuali.rice.kns.question.ConfirmationQuestion;
058    import org.kuali.rice.kns.service.BusinessObjectService;
059    import org.kuali.rice.kns.service.DataDictionaryService;
060    import org.kuali.rice.kns.service.KualiConfigurationService;
061    import org.kuali.rice.kns.service.KualiRuleService;
062    import org.kuali.rice.kns.util.GlobalVariables;
063    import org.kuali.rice.kns.util.MessageList;
064    import org.kuali.rice.kns.util.ObjectUtils;
065    import org.kuali.rice.kns.util.RiceKeyConstants;
066    import org.kuali.rice.kns.util.UrlFactory;
067    import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
068    
069    /**
070     * Struts Action for Accounts Payable documents.
071     */
072    public class AccountsPayableActionBase extends PurchasingAccountsPayableActionBase {
073        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountsPayableActionBase.class);
074    
075        @Override
076        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
077            PurchasingAccountsPayableFormBase baseForm = (PurchasingAccountsPayableFormBase) form;
078            
079            ActionForward fwd = super.execute(mapping, form, request, response);
080            
081            AccountsPayableDocumentBase document = (AccountsPayableDocumentBase) baseForm.getDocument();
082            boolean foundAccountExpiredWarning = false;
083            for(int i=0;i<GlobalVariables.getMessageList().size();i++){
084                if (StringUtils.equals(GlobalVariables.getMessageList().get(i).getErrorKey(),PurapKeyConstants.MESSAGE_CLOSED_OR_EXPIRED_ACCOUNTS_REPLACED)){
085                    foundAccountExpiredWarning = true;
086                }   
087            }
088            
089            if (!foundAccountExpiredWarning){
090                SpringContext.getBean(AccountsPayableService.class).generateExpiredOrClosedAccountWarning(document);
091            }
092                
093            return fwd;
094            
095        }
096        /**
097         * Performs refresh of objects after a lookup.
098         * 
099         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#refresh(org.apache.struts.action.ActionMapping,
100         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
101         */
102        public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
103            PurchasingAccountsPayableFormBase baseForm = (PurchasingAccountsPayableFormBase) form;
104            
105            AccountsPayableDocumentBase document = (AccountsPayableDocumentBase) baseForm.getDocument();
106    
107            if (StringUtils.equals(baseForm.getRefreshCaller(), VendorConstants.VENDOR_ADDRESS_LOOKUPABLE_IMPL)) {
108                if (StringUtils.isNotBlank(request.getParameter(KFSPropertyConstants.DOCUMENT + "." + PurapPropertyConstants.VENDOR_ADDRESS_ID))) {
109                    Integer vendorAddressGeneratedId = document.getVendorAddressGeneratedIdentifier();
110                    VendorAddress refreshVendorAddress = new VendorAddress();
111                    refreshVendorAddress.setVendorAddressGeneratedIdentifier(vendorAddressGeneratedId);
112                    refreshVendorAddress = (VendorAddress) SpringContext.getBean(BusinessObjectService.class).retrieve(refreshVendorAddress);
113                    document.templateVendorAddress(refreshVendorAddress);
114                }
115            }
116    
117            return super.refresh(mapping, form, request, response);
118        }
119    
120        /**
121         * Checks the continuation account indicator and generates warnings if continuation accounts were used to replace original
122         * accounts on the document.
123         * 
124         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
125         */
126        @Override
127        protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
128            super.loadDocument(kualiDocumentFormBase);
129            AccountsPayableDocument document = (AccountsPayableDocument) kualiDocumentFormBase.getDocument();
130    
131            SpringContext.getBean(AccountsPayableService.class).generateExpiredOrClosedAccountWarning(document);
132            
133            SpringContext.getBean(AccountsPayableService.class).updateItemList(document);
134            ((AccountsPayableFormBase) kualiDocumentFormBase).updateItemCounts();
135        }
136    
137        /**
138         * Perform calculation on item line.
139         * 
140         * @param mapping An ActionMapping
141         * @param form An ActionForm
142         * @param request The HttpServletRequest
143         * @param response The HttpServletResponse
144         * @return An ActionForward
145         */
146        @Override
147        public ActionForward calculate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
148            AccountsPayableFormBase apForm = (AccountsPayableFormBase) form;
149            AccountsPayableDocument apDoc = (AccountsPayableDocument) apForm.getDocument();
150    
151            // call precalculate
152            if (SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedPreCalculateAccountsPayableEvent(apDoc))) {
153                customCalculate(apDoc);
154    
155                // set calculated flag according to document type and status
156                if (apForm instanceof PaymentRequestForm && apDoc.getStatusCode().equals(PaymentRequestStatuses.AWAITING_TAX_REVIEW)) {
157                    // set calculated tax flag for tax area calculation 
158                    PaymentRequestForm preqForm = (PaymentRequestForm)apForm;
159                    preqForm.setCalculatedTax(true);
160                }
161                else {
162                    // set calculated flag for document calculation, whether or not the process calculation rule passes, since it only gives warning
163                    apForm.setCalculated(true);
164                }
165            }
166    
167            return super.calculate(mapping, form, request, response);
168        }
169    
170        @Override
171        public ActionForward clearAllTaxes(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
172            AccountsPayableFormBase payableForm = (AccountsPayableFormBase) form;
173            AccountsPayableDocument apDoc = (AccountsPayableDocument) payableForm.getDocument();
174    
175            SpringContext.getBean(PurapService.class).clearAllTaxes(apDoc);
176    
177            return super.clearAllTaxes(mapping, form, request, response);
178        }
179        
180        /**
181         * Checks if calculation is required. Currently it is required when it has not already been calculated and full document entry
182         * status has not already passed.
183         * 
184         * @param apForm A Form, which must inherit from <code>AccountsPayableFormBase</code>
185         * @return true if calculation is required, false otherwise
186         */
187        protected boolean requiresCaculate(AccountsPayableFormBase apForm) {
188            boolean requiresCalculate = true;
189            PurchasingAccountsPayableDocument purapDocument = (PurchasingAccountsPayableDocument) apForm.getDocument();
190            requiresCalculate = !apForm.isCalculated() && !SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(purapDocument);
191    
192            return requiresCalculate;
193        }
194    
195        /**
196         * Returns the current action name.
197         * 
198         * @return A String. Set to null!
199         */
200        public String getActionName() {
201            return null;
202        }
203    
204        /**
205         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#route(org.apache.struts.action.ActionMapping,
206         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
207         */
208        @Override
209        public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
210            AccountsPayableFormBase apForm = (AccountsPayableFormBase) form;
211    
212            // set the last update user id
213            AccountsPayableDocumentBase document = (AccountsPayableDocumentBase) apForm.getDocument();
214            document.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
215    
216            // if form is not yet calculated, return and prompt user to calculate
217            if (requiresCaculate(apForm)) {
218                GlobalVariables.getMessageMap().putError(KFSConstants.DOCUMENT_ERRORS, PurapKeyConstants.ERROR_APPROVE_REQUIRES_CALCULATE);
219                return mapping.findForward(KFSConstants.MAPPING_BASIC);
220            }
221    
222            // recalculate
223            customCalculate((AccountsPayableDocument) apForm.getDocument());
224    
225            // route
226            ActionForward forward = super.route(mapping, form, request, response);
227    
228            // if successful, then redirect back to init
229            boolean successMessageFound = false;
230            MessageList messageList = GlobalVariables.getMessageList();
231            for (int i = 0; i < messageList.size(); i++) {
232                if (StringUtils.equals(messageList.get(i).getErrorKey(), RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL)) {
233                    successMessageFound = true;
234                    break;
235                }
236            }
237    
238            if (successMessageFound) {
239                String basePath = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSConstants.APPLICATION_URL_KEY);
240    
241                Properties parameters = new Properties();
242                parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.DOC_HANDLER_METHOD);
243                parameters.put(KFSConstants.PARAMETER_COMMAND, "initiate");
244                parameters.put(KFSConstants.DOCUMENT_TYPE_NAME, apForm.getDocTypeName());
245    
246                String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + "purap" + this.getActionName() + ".do", parameters);
247                forward = new ActionForward(lookupUrl, true);
248            }
249    
250            return forward;
251        }
252    
253        /**
254         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#save(org.apache.struts.action.ActionMapping,
255         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
256         */
257        @Override
258        public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
259            AccountsPayableFormBase apForm = (AccountsPayableFormBase) form;
260    
261            if (!requiresCaculate(apForm)) {
262                return super.save(mapping, form, request, response);
263            }
264    
265            GlobalVariables.getMessageMap().putError(KFSConstants.DOCUMENT_ERRORS, PurapKeyConstants.ERROR_SAVE_REQUIRES_CALCULATE);
266            return mapping.findForward(KFSConstants.MAPPING_BASIC);
267    
268        }
269    
270        /**
271         * A wrapper method which prompts for a reason to hold a payment request or credit memo.
272         * 
273         * @param mapping An ActionMapping
274         * @param form An ActionForm
275         * @param request The HttpServletRequest
276         * @param response The HttpServletResponse
277         * @param questionType A String used to distinguish which question is being asked
278         * @param notePrefix A String explaining what action was taken, to be prepended to the note containing the reason, which gets
279         *        written to the document
280         * @param operation A one-word String description of the action to be taken, to be substituted into the message. (Can be an
281         *        empty String for some messages.)
282         * @param messageKey A key to the message which will appear on the question screen
283         * @param callback A PurQuestionCallback
284         * @return An ActionForward
285         * @throws Exception
286         */
287        protected ActionForward askQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionType, String notePrefix, String operation, String messageKey, PurQuestionCallback callback) throws Exception {
288            TreeMap<String, PurQuestionCallback> questionsAndCallbacks = new TreeMap<String, PurQuestionCallback>();
289            questionsAndCallbacks.put(questionType, callback);
290            return askQuestionWithInput(mapping, form, request, response, questionType, notePrefix, operation, messageKey, questionsAndCallbacks, "", mapping.findForward(KFSConstants.MAPPING_BASIC));
291        }
292    
293        /**
294         * Builds and asks questions which require text input by the user for a payment request or a credit memo.
295         * 
296         * @param mapping An ActionMapping
297         * @param form An ActionForm
298         * @param request The HttpServletRequest
299         * @param response The HttpServletResponse
300         * @param questionType A String used to distinguish which question is being asked
301         * @param notePrefix A String explaining what action was taken, to be prepended to the note containing the reason, which gets
302         *        written to the document
303         * @param operation A one-word String description of the action to be taken, to be substituted into the message. (Can be an
304         *        empty String for some messages.)
305         * @param messageKey A (whole) key to the message which will appear on the question screen
306         * @param questionsAndCallbacks A TreeMap associating the type of question to be asked and the type of callback which should
307         *        happen in that case
308         * @param messagePrefix The most general part of a key to a message text to be retrieved from KualiConfigurationService,
309         *        Describes a collection of questions.
310         * @param redirect An ActionForward to return to if done with questions
311         * @return An ActionForward
312         * @throws Exception
313         */
314        protected ActionForward askQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionType, String notePrefix, String operation, String messageKey, TreeMap<String, PurQuestionCallback> questionsAndCallbacks, String messagePrefix, ActionForward redirect) throws Exception {
315            KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
316            AccountsPayableDocumentBase apDocument = (AccountsPayableDocumentBase) kualiDocumentFormBase.getDocument();
317    
318            String question = (String) request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
319            String reason = request.getParameter(KFSConstants.QUESTION_REASON_ATTRIBUTE_NAME);
320            String noteText = "";
321    
322            KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class);
323            String firstQuestion = questionsAndCallbacks.firstKey();
324            PurQuestionCallback callback = null;
325            Iterator questions = questionsAndCallbacks.keySet().iterator();
326            String mapQuestion = null;
327            String key = null;
328    
329            // Start in logic for confirming the close.
330            if (question == null) {
331                key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, firstQuestion);
332                String message = StringUtils.replace(key, "{0}", operation);
333    
334                // Ask question if not already asked.
335                return this.performQuestionWithInput(mapping, form, request, response, firstQuestion, message, KFSConstants.CONFIRMATION_QUESTION, questionType, "");
336            }
337            else {
338                // find callback for this question
339                while (questions.hasNext()) {
340                    mapQuestion = (String) questions.next();
341    
342                    if (StringUtils.equals(mapQuestion, question)) {
343                        callback = questionsAndCallbacks.get(mapQuestion);
344                        break;
345                    }
346                }
347                key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, mapQuestion);
348    
349                Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
350                if (question.equals(mapQuestion) && buttonClicked.equals(ConfirmationQuestion.NO)) {
351                    // If 'No' is the button clicked, just reload the doc
352    
353                    String nextQuestion = null;
354                    // ask another question if more left
355                    if (questions.hasNext()) {
356                        nextQuestion = (String) questions.next();
357                        key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, nextQuestion);
358    
359                        return this.performQuestionWithInput(mapping, form, request, response, nextQuestion, key, KFSConstants.CONFIRMATION_QUESTION, questionType, "");
360                    }
361                    else {
362    
363                        return mapping.findForward(KFSConstants.MAPPING_BASIC);
364                    }
365                }
366                // Have to check length on value entered.
367                String introNoteMessage = notePrefix + KFSConstants.BLANK_SPACE;
368    
369                // Build out full message.
370                noteText = introNoteMessage + reason;
371                int noteTextLength = noteText.length();
372    
373                // Get note text max length from DD.
374                int noteTextMaxLength = SpringContext.getBean(DataDictionaryService.class).getAttributeMaxLength(Note.class, KFSConstants.NOTE_TEXT_PROPERTY_NAME).intValue();
375                if (StringUtils.isBlank(reason) || (noteTextLength > noteTextMaxLength)) {
376                    // Figure out exact number of characters that the user can enter.
377                    int reasonLimit = noteTextMaxLength - noteTextLength;
378                    if (reason == null) {
379                        // Prevent a NPE by setting the reason to a blank string.
380                        reason = "";
381                    }
382    
383                    return this.performQuestionWithInputAgainBecauseOfErrors(mapping, form, request, response, mapQuestion, key, KFSConstants.CONFIRMATION_QUESTION, questionType, "", reason, PurapKeyConstants.ERROR_PAYMENT_REQUEST_REASON_REQUIRED, KFSConstants.QUESTION_REASON_ATTRIBUTE_NAME, new Integer(reasonLimit).toString());
384                }
385            }
386    
387            // make callback
388            if (ObjectUtils.isNotNull(callback)) {
389                AccountsPayableDocument refreshedApDocument = callback.doPostQuestion(apDocument, noteText);
390                kualiDocumentFormBase.setDocument(refreshedApDocument);
391            }
392            String nextQuestion = null;
393            // ask another question if more left
394            if (questions.hasNext()) {
395                nextQuestion = (String) questions.next();
396                key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, nextQuestion);
397    
398                return this.performQuestionWithInput(mapping, form, request, response, nextQuestion, key, KFSConstants.CONFIRMATION_QUESTION, questionType, "");
399            }
400    
401            return redirect;
402        }
403    
404        /**
405         * Used to look up messages to be displayed, from the KualiConfigurationService, given either a whole key or two parts of a key
406         * that may be concatenated together.
407         * 
408         * @param messageKey String. One of the message keys in PurapKeyConstants.
409         * @param messagePrefix String. A prefix to the question key, such as "ap.question." that, concatenated with the question,
410         *        comprises the whole key of the message.
411         * @param kualiConfiguration An instance of KualiConfigurationService
412         * @param question String. The most specific part of the message key in PurapKeyConstants.
413         * @return The message to be displayed given the key
414         */
415        protected String getQuestionProperty(String messageKey, String messagePrefix, KualiConfigurationService kualiConfiguration, String question) {
416            return kualiConfiguration.getPropertyString((StringUtils.isEmpty(messagePrefix)) ? messageKey : messagePrefix + question);
417        }
418    
419        public ActionForward reopenPo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
420            LOG.debug("Reopen PO started");
421            return askQuestionsAndPerformReopenPurchaseOrder(mapping, form, request, response);
422        }
423    
424        /**
425         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#cancel(org.apache.struts.action.ActionMapping,
426         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
427         */
428        @Override
429        public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
430            return askCancelQuestion(mapping, form, request, response);
431        }
432    
433        /**
434         * Constructs and asks the question as to whether the user wants to cancel, for payment requests and credit memos.
435         * 
436         * @param mapping An ActionMapping
437         * @param form An ActionForm
438         * @param request The HttpServletRequest
439         * @param response The HttpServletResponse
440         * @return An ActionForward
441         * @throws Exception
442         */
443        protected ActionForward askCancelQuestion(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
444            PurchasingAccountsPayableFormBase apForm = (PurchasingAccountsPayableFormBase) form;
445            
446            String operation = "Cancel ";
447            PurQuestionCallback callback = cancelCallbackMethod();
448            TreeMap<String, PurQuestionCallback> questionsAndCallbacks = new TreeMap<String, PurQuestionCallback>();
449            questionsAndCallbacks.put("cancelAP", callback);
450    
451            return askQuestionWithInput(mapping, form, request, response, CMDocumentsStrings.CANCEL_CM_QUESTION, AccountsPayableDocumentStrings.CANCEL_NOTE_PREFIX, operation, PurapKeyConstants.CREDIT_MEMO_QUESTION_CANCEL_DOCUMENT, questionsAndCallbacks, PurapKeyConstants.AP_QUESTION_PREFIX, mapping.findForward(KFSConstants.MAPPING_PORTAL));
452        }
453    
454        /**
455         * Returns a question callback for the Cancel Purchase Order action.
456         * 
457         * @return A PurQuestionCallback with a post-question activity appropriate to the Cancel PO action
458         */
459        protected PurQuestionCallback cancelPOActionCallbackMethod() {
460    
461            return new PurQuestionCallback() {
462                public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
463                    // base impl do nothing
464                    return document;
465                }
466            };
467        }
468    
469        /**
470         * Returns a question callback for the Cancel action.
471         * 
472         * @return A PurQuestionCallback which does post-question tasks appropriate to Cancellation.
473         */
474        protected PurQuestionCallback cancelCallbackMethod() {
475            return new PurQuestionCallback() {
476                public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
477                    SpringContext.getBean(AccountsPayableService.class).cancelAccountsPayableDocumentByCheckingDocumentStatus(document, noteText);
478                    return document;
479                }
480            };
481        }
482    
483        protected ActionForward askQuestionsAndPerformReopenPurchaseOrder(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
484            LOG.debug("askQuestionsAndPerformDocumentAction started.");
485            KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
486            AccountsPayableDocumentBase apDoc = (AccountsPayableDocumentBase) kualiDocumentFormBase.getDocument();
487            Object question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
488            String questionType = PODocumentsStrings.REOPEN_PO_QUESTION;
489            String confirmType = PODocumentsStrings.CONFIRM_REOPEN_QUESTION;
490            String messageType = PurapKeyConstants.PURCHASE_ORDER_MESSAGE_REOPEN_DOCUMENT;
491            String operation = "Reopen ";
492    
493            try {
494                KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class);
495    
496                // Start in logic for confirming the proposed operation.
497                if (ObjectUtils.isNull(question)) {
498                    String key = kualiConfiguration.getPropertyString(PurapKeyConstants.PURCHASE_ORDER_QUESTION_DOCUMENT);
499                    String message = StringUtils.replace(key, "{0}", operation);
500                    return this.performQuestionWithoutInput(mapping, form, request, response, questionType, message, KFSConstants.CONFIRMATION_QUESTION, questionType, "");
501                }
502                else {
503                    Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
504                    if (question.equals(questionType) && buttonClicked.equals(ConfirmationQuestion.NO)) {
505                        // If 'No' is the button clicked, just reload the doc
506                        return mapping.findForward(KFSConstants.MAPPING_BASIC);
507                    }
508                    else if (question.equals(confirmType) && buttonClicked.equals(SingleConfirmationQuestion.OK)) {
509                        // This is the case when the user clicks on "OK" in the end; redirect to the preq doc
510                        return mapping.findForward(KFSConstants.MAPPING_BASIC);
511                    }
512                }
513    
514                PurchaseOrderDocument po = apDoc.getPurchaseOrderDocument();
515                if (!po.isPendingActionIndicator() && PurapConstants.PurchaseOrderStatuses.CLOSED.equals(po.getStatusCode())) {
516                    /*
517                     * Below if-else code block calls PurchaseOrderService methods that will throw ValidationException objects if errors
518                     * occur during any process in the attempt to perform its actions. Assume, if these return successfully, that the
519                     * PurchaseOrderDocument object returned from each is the newly created document and that all actions in the method
520                     * were run correctly. NOTE: IF BELOW IF-ELSE IS EDITED THE NEW METHODS CALLED MUST THROW ValidationException OBJECT
521                     * IF AN ERROR IS ADDED TO THE GlobalVariables
522                     */
523                    po = initiateReopenPurchaseOrder(po, kualiDocumentFormBase.getAnnotation());
524    
525                    if (!GlobalVariables.getMessageMap().hasNoErrors()) {
526                        throw new ValidationException("errors occurred during new PO creation");
527                    }
528    
529                    if (StringUtils.isNotEmpty(messageType)) {
530                        GlobalVariables.getMessageList().add(messageType);
531                    }
532                    return this.performQuestionWithoutInput(mapping, form, request, response, confirmType, kualiConfiguration.getPropertyString(messageType), PODocumentsStrings.SINGLE_CONFIRMATION_QUESTION, questionType, "");
533                }
534                else {
535                    return this.performQuestionWithoutInput(mapping, form, request, response, confirmType, "Unable to reopen the PO at this time due to the incorrect PO status or a pending PO change document.", PODocumentsStrings.SINGLE_CONFIRMATION_QUESTION, questionType, "");
536                }
537    
538            }
539            catch (ValidationException ve) {
540                throw ve;
541            }
542        }
543    
544        public PurchaseOrderDocument initiateReopenPurchaseOrder(PurchaseOrderDocument po, String annotation) {
545            try {
546                LogicContainer logicToRun = new LogicContainer() {
547                    public Object runLogic(Object[] objects) throws Exception {
548                        PurchaseOrderDocument po = (PurchaseOrderDocument) objects[0];
549    
550                        Note cancelNote = new Note();
551                        cancelNote.setAuthorUniversalIdentifier(GlobalVariables.getUserSession().getPerson().getPrincipalId());
552                        cancelNote.setNoteText(SpringContext.getBean(KualiConfigurationService.class).getPropertyString(PurapKeyConstants.AP_REOPENS_PURCHASE_ORDER_NOTE));
553                        po.addNote(cancelNote);
554                        SpringContext.getBean(PurapService.class).saveDocumentNoValidation(po);
555    
556                        return SpringContext.getBean(PurchaseOrderService.class).createAndRoutePotentialChangeDocument(po.getDocumentNumber(), PurchaseOrderDocTypes.PURCHASE_ORDER_REOPEN_DOCUMENT, (String) objects[1], null, PurchaseOrderStatuses.PENDING_REOPEN);
557                    }
558                };
559                return (PurchaseOrderDocument) SpringContext.getBean(PurapService.class).performLogicWithFakedUserSession(KFSConstants.SYSTEM_USER, logicToRun, new Object[] { po, annotation });
560            }
561            catch (WorkflowException e) {
562                String errorMsg = "Workflow Exception caught: " + e.getLocalizedMessage();
563                LOG.error(errorMsg, e);
564                throw new RuntimeException(errorMsg, e);
565            }
566            catch (Exception e) {
567                throw new RuntimeException(e);
568            }
569        }
570    
571    }