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