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.fp.document.web.struts;
017    
018    import java.io.ByteArrayOutputStream;
019    import java.util.Iterator;
020    import java.util.List;
021    
022    import javax.servlet.http.HttpServletRequest;
023    import javax.servlet.http.HttpServletResponse;
024    
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.fp.businessobject.Check;
029    import org.kuali.kfs.fp.businessobject.CoinDetail;
030    import org.kuali.kfs.fp.businessobject.CurrencyDetail;
031    import org.kuali.kfs.fp.document.CashReceiptDocument;
032    import org.kuali.kfs.fp.document.service.CashReceiptCoverSheetService;
033    import org.kuali.kfs.fp.document.service.CashReceiptService;
034    import org.kuali.kfs.fp.document.validation.event.AddCheckEvent;
035    import org.kuali.kfs.fp.document.validation.event.DeleteCheckEvent;
036    import org.kuali.kfs.fp.document.validation.event.UpdateCheckEvent;
037    import org.kuali.kfs.sys.KFSConstants;
038    import org.kuali.kfs.sys.KFSKeyConstants;
039    import org.kuali.kfs.sys.KFSPropertyConstants;
040    import org.kuali.kfs.sys.context.SpringContext;
041    import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase;
042    import org.kuali.rice.kew.exception.WorkflowException;
043    import org.kuali.rice.kns.service.DocumentService;
044    import org.kuali.rice.kns.service.KualiConfigurationService;
045    import org.kuali.rice.kns.service.KualiRuleService;
046    import org.kuali.rice.kns.util.GlobalVariables;
047    import org.kuali.rice.kns.util.Timer;
048    import org.kuali.rice.kns.util.WebUtils;
049    import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
050    
051    /**
052     * 
053     */
054    public class CashReceiptAction extends KualiAccountingDocumentActionBase {
055        /**
056         * Adds handling for check updates
057         * 
058         * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm,
059         *      javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
060         */
061        @Override
062        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
063            Timer t0 = new Timer("execute");
064            CashReceiptForm cform = (CashReceiptForm) form;
065    
066            if (cform.hasDocumentId()) {
067                CashReceiptDocument cdoc = cform.getCashReceiptDocument();
068    
069                // handle change of checkEntryMode
070                processCheckEntryMode(cform, cdoc);
071    
072                // handle changes to checks (but only if current checkEntryMode is 'detail')
073                if (CashReceiptDocument.CHECK_ENTRY_DETAIL.equals(cdoc.getCheckEntryMode())) {
074                    cdoc.setTotalCheckAmount(cdoc.calculateCheckTotal()); // recalc b/c changes to the amounts could have happened
075                    processChecks(cdoc, cform);
076                }
077    
078                // generate errors for negative cash totals (especially for the recalculate button)
079                SpringContext.getBean(CashReceiptService.class).areCashTotalsInvalid(cdoc);
080            }
081    
082            // proceed as usual
083            ActionForward result = super.execute(mapping, form, request, response);
084            t0.log();
085            return result;
086    
087        }
088    
089        /**
090         * Prepares and streams CR PDF Cover Sheet
091         * 
092         * @param mapping
093         * @param form
094         * @param request
095         * @param response
096         * @return ActionForward
097         * @throws Exception
098         */
099        public ActionForward printCoverSheet(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
100            CashReceiptForm crForm = (CashReceiptForm) form;
101            
102            // get directory of tempate
103            String directory = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSConstants.EXTERNALIZABLE_HELP_URL_KEY);
104    
105            // retrieve document
106            String documentNumber = request.getParameter(KFSPropertyConstants.DOCUMENT_NUMBER);
107    
108            CashReceiptDocument document = (CashReceiptDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(documentNumber);
109    
110            // since this action isn't triggered by a post, we don't have the normal document data
111            // so we have to set the document into the form manually so that later authz processing
112            // has a document object instance to work with
113            crForm.setDocument(document);
114    
115            ByteArrayOutputStream baos = new ByteArrayOutputStream();
116            CashReceiptCoverSheetService coverSheetService = SpringContext.getBean(CashReceiptCoverSheetService.class);
117            coverSheetService.generateCoverSheet(document, directory, baos);
118            String fileName = documentNumber + "_cover_sheet.pdf";
119            WebUtils.saveMimeOutputStreamAsFile(response, "application/pdf", baos, fileName);
120    
121            return null;
122        }
123    
124        /**
125         * This method processes the check entry mode to determine if the user is entering checks or if they are just entering the
126         * total.
127         * 
128         * @param crForm
129         * @param crDoc
130         */
131        protected void processCheckEntryMode(CashReceiptForm crForm, CashReceiptDocument crDoc) {
132            String formMode = crForm.getCheckEntryMode();
133            String docMode = crDoc.getCheckEntryMode();
134    
135            if (CashReceiptDocument.CHECK_ENTRY_DETAIL.equals(formMode) || CashReceiptDocument.CHECK_ENTRY_TOTAL.equals(formMode)) {
136                if (!formMode.equals(docMode)) {
137                    if (formMode.equals(CashReceiptDocument.CHECK_ENTRY_DETAIL)) {
138                        // save current checkTotal, for future restoration
139                        crForm.setCheckTotal(crDoc.getTotalCheckAmount());
140    
141                        // change mode
142                        crDoc.setCheckEntryMode(formMode);
143                        crDoc.setTotalCheckAmount(crDoc.calculateCheckTotal());
144    
145                        // notify user
146                        GlobalVariables.getMessageList().add(KFSKeyConstants.CashReceipt.MSG_CHECK_ENTRY_INDIVIDUAL);
147                    }
148                    else {
149                        // restore saved checkTotal
150                        crDoc.setTotalCheckAmount(crForm.getCheckTotal());
151    
152                        // change mode
153                        crDoc.setCheckEntryMode(formMode);
154    
155                        // notify user
156                        GlobalVariables.getMessageList().add(KFSKeyConstants.CashReceipt.MSG_CHECK_ENTRY_TOTAL);
157                    }
158                }
159            }
160        }
161    
162        /**
163         * This method handles iterating over the check list and generating check events to apply rules to.
164         * 
165         * @param cdoc
166         * @param cform
167         */
168        protected void processChecks(CashReceiptDocument cdoc, CashReceiptForm cform) {
169            List formChecks = cdoc.getChecks();
170    
171            int index = 0;
172            Iterator i = formChecks.iterator();
173            while (i.hasNext()) {
174                Check formCheck = (Check) i.next();
175    
176                // only generate update events for specific action methods
177                String methodToCall = cform.getMethodToCall();
178                if (UPDATE_EVENT_ACTIONS.contains(methodToCall)) {
179                    SpringContext.getBean(KualiRuleService.class).applyRules(new UpdateCheckEvent(KFSPropertyConstants.DOCUMENT + "." + KFSPropertyConstants.CHECK + "[" + index + "]", cdoc, formCheck));
180                }
181                index++;
182            }
183        }
184    
185        /**
186         * Adds Check instance created from the current "new check" line to the document
187         * 
188         * @param mapping
189         * @param form
190         * @param request
191         * @param response
192         * @return ActionForward
193         * @throws Exception
194         */
195        public ActionForward addCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
196            CashReceiptForm crForm = (CashReceiptForm) form;
197            CashReceiptDocument crDoc = crForm.getCashReceiptDocument();
198    
199            Check newCheck = crForm.getNewCheck();
200            newCheck.setDocumentNumber(crDoc.getDocumentNumber());
201    
202            // check business rules
203            boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddCheckEvent(KFSConstants.NEW_CHECK_PROPERTY_NAME, crDoc, newCheck));
204            if (rulePassed) {
205                // add check
206                crDoc.addCheck(newCheck);
207    
208                // clear the used newCheck
209                crForm.setNewCheck(crDoc.createNewCheck());
210            }
211    
212            return mapping.findForward(KFSConstants.MAPPING_BASIC);
213        }
214    
215        /**
216         * Deletes the selected check (line) from the document
217         * 
218         * @param mapping
219         * @param form
220         * @param request
221         * @param response
222         * @return ActionForward
223         * @throws Exception
224         */
225        public ActionForward deleteCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
226            CashReceiptForm crForm = (CashReceiptForm) form;
227            CashReceiptDocument crDoc = crForm.getCashReceiptDocument();
228    
229            int deleteIndex = getLineToDelete(request);
230            Check oldCheck = crDoc.getCheck(deleteIndex);
231    
232    
233            boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new DeleteCheckEvent(KFSConstants.EXISTING_CHECK_PROPERTY_NAME, crDoc, oldCheck));
234    
235            if (rulePassed) {
236                // delete check
237                crDoc.removeCheck(deleteIndex);
238    
239                // delete baseline check, if any
240                if (crForm.hasBaselineCheck(deleteIndex)) {
241                    crForm.getBaselineChecks().remove(deleteIndex);
242                }
243            }
244            else {
245                GlobalVariables.getMessageMap().putError("document.check[" + deleteIndex + "]", KFSKeyConstants.Check.ERROR_CHECK_DELETERULE, Integer.toString(deleteIndex));
246            }
247    
248            return mapping.findForward(KFSConstants.MAPPING_BASIC);
249        }
250    
251    
252        /**
253         * Changes the current check-entry mode, if necessary
254         * 
255         * @param mapping
256         * @param form
257         * @param request
258         * @param response
259         * @return ActionForward
260         * @throws Exception
261         */
262        public ActionForward changeCheckEntryMode(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
263    
264            CashReceiptForm crForm = (CashReceiptForm) form;
265            CashReceiptDocument crDoc = crForm.getCashReceiptDocument();
266    
267            String formMode = crForm.getCheckEntryMode();
268            String docMode = crDoc.getCheckEntryMode();
269    
270            if (CashReceiptDocument.CHECK_ENTRY_DETAIL.equals(formMode) || CashReceiptDocument.CHECK_ENTRY_TOTAL.equals(formMode)) {
271                if (!formMode.equals(docMode)) {
272    
273                    if (formMode.equals(CashReceiptDocument.CHECK_ENTRY_DETAIL)) {
274                        // save current checkTotal, for future restoration
275                        crForm.setCheckTotal(crDoc.getTotalCheckAmount());
276    
277                        // change mode
278                        crDoc.setCheckEntryMode(formMode);
279                        crDoc.setTotalCheckAmount(crDoc.calculateCheckTotal());
280    
281                        // notify user
282                        GlobalVariables.getMessageList().add(KFSKeyConstants.CashReceipt.MSG_CHECK_ENTRY_INDIVIDUAL);
283                    }
284                    else {
285                        // restore saved checkTotal
286                        crDoc.setTotalCheckAmount(crForm.getCheckTotal());
287    
288                        // change mode
289                        crDoc.setCheckEntryMode(formMode);
290    
291                        // notify user
292                        GlobalVariables.getMessageList().add(KFSKeyConstants.CashReceipt.MSG_CHECK_ENTRY_TOTAL);
293                    }
294                }
295            }
296    
297            return mapping.findForward(KFSConstants.MAPPING_BASIC);
298        }
299    
300    
301        /**
302         * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#createDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
303         */
304        @Override
305        protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
306            super.createDocument(kualiDocumentFormBase);
307    
308            CashReceiptForm crForm = (CashReceiptForm) kualiDocumentFormBase;
309            CashReceiptDocument crDoc = crForm.getCashReceiptDocument();
310    
311            CashReceiptService crs = SpringContext.getBean(CashReceiptService.class);         
312            crDoc.initializeCampusLocationCode();
313    
314            /* initialize currency and coin detail */
315            CurrencyDetail currencyDetail = new CurrencyDetail();
316            currencyDetail.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.CASH_RECEIPTS);
317            currencyDetail.setFinancialDocumentTypeCode(CashReceiptDocument.DOCUMENT_TYPE);
318            currencyDetail.setDocumentNumber(crDoc.getDocumentNumber());
319            crDoc.setCurrencyDetail(currencyDetail);
320    
321            CoinDetail coinDetail = new CoinDetail();
322            coinDetail.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.CASH_RECEIPTS);
323            coinDetail.setFinancialDocumentTypeCode(CashReceiptDocument.DOCUMENT_TYPE);
324            coinDetail.setDocumentNumber(crDoc.getDocumentNumber());
325            crDoc.setCoinDetail(coinDetail);
326    
327            initDerivedCheckValues(crForm);
328        }
329    
330        /**
331         * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
332         */
333        @Override
334        protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
335            super.loadDocument(kualiDocumentFormBase);
336    
337            initDerivedCheckValues((CashReceiptForm) kualiDocumentFormBase);
338        }
339    
340        /**
341         * Initializes form values which must be derived form document contents (i.e. those which aren't directly available from the
342         * document)
343         * 
344         * @param cform
345         */
346        protected void initDerivedCheckValues(CashReceiptForm cform) {
347            CashReceiptDocument cdoc = cform.getCashReceiptDocument();
348    
349            cform.setCheckEntryMode(cdoc.getCheckEntryMode());
350            cform.setCheckTotal(cdoc.getTotalCheckAmount());
351    
352            cform.getBaselineChecks().clear();
353            cform.getBaselineChecks().addAll(cform.getCashReceiptDocument().getChecks());
354        }
355    
356        /**
357         * Overridden to guarantee that form of copied document is set to whatever the entry mode of the document is
358         * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#copy(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
359         */
360        @Override
361        public ActionForward copy(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
362            ActionForward forward = super.copy(mapping, form, request, response);
363            initDerivedCheckValues((CashReceiptForm)form);
364            return forward;
365        }
366    }
367