001/*
002 * Copyright 2008 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 */
016package org.kuali.ole.module.purap.document.web.struts;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.struts.action.ActionForm;
020import org.apache.struts.action.ActionForward;
021import org.apache.struts.action.ActionMapping;
022import org.kuali.ole.module.purap.PurapKeyConstants;
023import org.kuali.ole.module.purap.document.ReceivingDocument;
024import org.kuali.ole.module.purap.util.ReceivingQuestionCallback;
025import org.kuali.ole.sys.OLEConstants;
026import org.kuali.ole.sys.context.SpringContext;
027import org.kuali.ole.sys.document.web.struts.FinancialSystemTransactionalDocumentActionBase;
028import org.kuali.rice.core.api.config.property.ConfigurationService;
029import org.kuali.rice.kns.question.ConfirmationQuestion;
030import org.kuali.rice.kns.service.DataDictionaryService;
031import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
032import org.kuali.rice.krad.bo.Note;
033import org.kuali.rice.krad.util.ObjectUtils;
034
035import javax.servlet.http.HttpServletRequest;
036import javax.servlet.http.HttpServletResponse;
037import java.util.Iterator;
038import java.util.TreeMap;
039
040public class ReceivingBaseAction extends FinancialSystemTransactionalDocumentActionBase {
041
042    /**
043     * A wrapper method which prompts for a reason to hold a payment request or credit memo.
044     *
045     * @param mapping      An ActionMapping
046     * @param form         An ActionForm
047     * @param request      The HttpServletRequest
048     * @param response     The HttpServletResponse
049     * @param questionType A String used to distinguish which question is being asked
050     * @param notePrefix   A String explaining what action was taken, to be prepended to the note containing the reason, which gets
051     *                     written to the document
052     * @param operation    A one-word String description of the action to be taken, to be substituted into the message. (Can be an
053     *                     empty String for some messages.)
054     * @param messageKey   A key to the message which will appear on the question screen
055     * @param callback     A PurQuestionCallback
056     * @return An ActionForward
057     * @throws Exception
058     */
059    protected ActionForward askQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionType, String notePrefix, String operation, String messageKey, ReceivingQuestionCallback callback) throws Exception {
060        TreeMap<String, ReceivingQuestionCallback> questionsAndCallbacks = new TreeMap<String, ReceivingQuestionCallback>();
061        questionsAndCallbacks.put(questionType, callback);
062
063        return askQuestionWithInput(mapping, form, request, response, questionType, notePrefix, operation, messageKey, questionsAndCallbacks, "", mapping.findForward(OLEConstants.MAPPING_BASIC));
064    }
065
066    /**
067     * Builds and asks questions which require text input by the user for a payment request or a credit memo.
068     *
069     * @param mapping               An ActionMapping
070     * @param form                  An ActionForm
071     * @param request               The HttpServletRequest
072     * @param response              The HttpServletResponse
073     * @param questionType          A String used to distinguish which question is being asked
074     * @param notePrefix            A String explaining what action was taken, to be prepended to the note containing the reason, which gets
075     *                              written to the document
076     * @param operation             A one-word String description of the action to be taken, to be substituted into the message. (Can be an
077     *                              empty String for some messages.)
078     * @param messageKey            A (whole) key to the message which will appear on the question screen
079     * @param questionsAndCallbacks A TreeMap associating the type of question to be asked and the type of callback which should
080     *                              happen in that case
081     * @param messagePrefix         The most general part of a key to a message text to be retrieved from ConfigurationService,
082     *                              Describes a collection of questions.
083     * @param redirect              An ActionForward to return to if done with questions
084     * @return An ActionForward
085     * @throws Exception
086     */
087    protected ActionForward askQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionType, String notePrefix, String operation, String messageKey, TreeMap<String, ReceivingQuestionCallback> questionsAndCallbacks, String messagePrefix, ActionForward redirect) throws Exception {
088        KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
089        ReceivingDocument receivingDocument = (ReceivingDocument) kualiDocumentFormBase.getDocument();
090
091        String question = (String) request.getParameter(OLEConstants.QUESTION_INST_ATTRIBUTE_NAME);
092        String reason = request.getParameter(OLEConstants.QUESTION_REASON_ATTRIBUTE_NAME);
093        String noteText = "";
094
095        ConfigurationService kualiConfiguration = SpringContext.getBean(ConfigurationService.class);
096        String firstQuestion = questionsAndCallbacks.firstKey();
097        ReceivingQuestionCallback callback = null;
098        Iterator questions = questionsAndCallbacks.keySet().iterator();
099        String mapQuestion = null;
100        String key = null;
101
102        // Start in logic for confirming the close.
103        if (question == null) {
104            key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, firstQuestion);
105            String message = StringUtils.replace(key, "{0}", operation);
106
107            // Ask question if not already asked.
108            return this.performQuestionWithInput(mapping, form, request, response, firstQuestion, message, OLEConstants.CONFIRMATION_QUESTION, questionType, "");
109        } else {
110            // find callback for this question
111            while (questions.hasNext()) {
112                mapQuestion = (String) questions.next();
113
114                if (StringUtils.equals(mapQuestion, question)) {
115                    callback = questionsAndCallbacks.get(mapQuestion);
116                    break;
117                }
118            }
119            key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, mapQuestion);
120
121            Object buttonClicked = request.getParameter(OLEConstants.QUESTION_CLICKED_BUTTON);
122            if (question.equals(mapQuestion) && buttonClicked.equals(ConfirmationQuestion.NO)) {
123                // If 'No' is the button clicked, just reload the doc
124
125                String nextQuestion = null;
126                // ask another question if more left
127                if (questions.hasNext()) {
128                    nextQuestion = (String) questions.next();
129                    key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, nextQuestion);
130
131                    return this.performQuestionWithInput(mapping, form, request, response, nextQuestion, key, OLEConstants.CONFIRMATION_QUESTION, questionType, "");
132                } else {
133
134                    return mapping.findForward(OLEConstants.MAPPING_BASIC);
135                }
136            }
137            // Have to check length on value entered.
138            String introNoteMessage = notePrefix + OLEConstants.BLANK_SPACE;
139
140            // Build out full message.
141            noteText = introNoteMessage + reason;
142            int noteTextLength = noteText.length();
143
144            // Get note text max length from DD.
145            int noteTextMaxLength = SpringContext.getBean(DataDictionaryService.class).getAttributeMaxLength(Note.class, OLEConstants.NOTE_TEXT_PROPERTY_NAME).intValue();
146            if (StringUtils.isBlank(reason) || (noteTextLength > noteTextMaxLength)) {
147                // Figure out exact number of characters that the user can enter.
148                int reasonLimit = noteTextMaxLength - noteTextLength;
149                if (reason == null) {
150                    // Prevent a NPE by setting the reason to a blank string.
151                    reason = "";
152                }
153
154                return this.performQuestionWithInputAgainBecauseOfErrors(mapping, form, request, response, mapQuestion, key, OLEConstants.CONFIRMATION_QUESTION, questionType, "", reason, PurapKeyConstants.ERROR_PAYMENT_REQUEST_REASON_REQUIRED, OLEConstants.QUESTION_REASON_ATTRIBUTE_NAME, new Integer(reasonLimit).toString());
155            }
156        }
157
158        // make callback
159        if (ObjectUtils.isNotNull(callback)) {
160            ReceivingDocument refreshedReceivingDocument = callback.doPostQuestion(receivingDocument, noteText);
161            kualiDocumentFormBase.setDocument(refreshedReceivingDocument);
162        }
163        String nextQuestion = null;
164        // ask another question if more left
165        if (questions.hasNext()) {
166            nextQuestion = (String) questions.next();
167            key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, nextQuestion);
168
169            return this.performQuestionWithInput(mapping, form, request, response, nextQuestion, key, OLEConstants.CONFIRMATION_QUESTION, questionType, "");
170        }
171
172        return redirect;
173    }
174
175    /**
176     * Used to look up messages to be displayed, from the ConfigurationService, given either a whole key or two parts of a key
177     * that may be concatenated together.
178     *
179     * @param messageKey         String. One of the message keys in PurapKeyConstants.
180     * @param messagePrefix      String. A prefix to the question key, such as "ap.question." that, concatenated with the question,
181     *                           comprises the whole key of the message.
182     * @param kualiConfiguration An instance of ConfigurationService
183     * @param question           String. The most specific part of the message key in PurapKeyConstants.
184     * @return The message to be displayed given the key
185     */
186    protected String getQuestionProperty(String messageKey, String messagePrefix, ConfigurationService kualiConfiguration, String question) {
187
188        return kualiConfiguration.getPropertyValueAsString((StringUtils.isEmpty(messagePrefix)) ? messageKey : messagePrefix + question);
189    }
190
191}