001/*
002 * Copyright 2007 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.apache.struts.upload.FormFile;
023import org.kuali.ole.module.purap.PurapConstants;
024import org.kuali.ole.module.purap.PurapPropertyConstants;
025import org.kuali.ole.module.purap.businessobject.PurApAccountingLine;
026import org.kuali.ole.module.purap.businessobject.PurApAccountingLineParser;
027import org.kuali.ole.module.purap.businessobject.PurApItem;
028import org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument;
029import org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocumentBase;
030import org.kuali.ole.module.purap.document.service.PurapService;
031import org.kuali.ole.module.purap.service.PurapAccountingService;
032import org.kuali.ole.sys.OLEConstants;
033import org.kuali.ole.sys.OLEPropertyConstants;
034import org.kuali.ole.sys.businessobject.AccountingLine;
035import org.kuali.ole.sys.businessobject.SourceAccountingLine;
036import org.kuali.ole.sys.context.SpringContext;
037import org.kuali.ole.sys.document.validation.event.AddAccountingLineEvent;
038import org.kuali.ole.sys.exception.AccountingLineParserException;
039import org.kuali.ole.sys.web.struts.KualiAccountingDocumentActionBase;
040import org.kuali.ole.sys.web.struts.KualiAccountingDocumentFormBase;
041import org.kuali.rice.core.api.util.RiceConstants;
042import org.kuali.rice.kew.api.WorkflowDocument;
043import org.kuali.rice.kew.api.exception.WorkflowException;
044import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
045import org.kuali.rice.kns.web.struts.form.KualiForm;
046import org.kuali.rice.krad.service.DocumentService;
047import org.kuali.rice.krad.service.KualiRuleService;
048import org.kuali.rice.krad.service.PersistenceService;
049import org.kuali.rice.krad.util.GlobalVariables;
050import org.kuali.rice.krad.util.ObjectUtils;
051
052import javax.servlet.http.HttpServletRequest;
053import javax.servlet.http.HttpServletResponse;
054import java.io.FileNotFoundException;
055import java.io.IOException;
056import java.math.BigDecimal;
057import java.util.HashMap;
058import java.util.Iterator;
059import java.util.List;
060import java.util.Map;
061import java.util.Map.Entry;
062
063/**
064 * Struts Action for Purchasing and Accounts Payable documents
065 */
066public class PurchasingAccountsPayableActionBase extends KualiAccountingDocumentActionBase {
067
068    /**
069     * @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)
070     */
071    @Override
072    public ActionForward copy(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
073        ActionForward actionForward = super.copy(mapping, form, request, response);
074
075
076        KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
077        PurchasingAccountsPayableDocument purapDocument = (PurchasingAccountsPayableDocument) kualiDocumentFormBase.getDocument();
078
079        //refresh accounts in each item....
080        List<PurApItem> items = purapDocument.getItems();
081
082        for (PurApItem item : items) {
083            item.getNewSourceLine().setAccountLinePercent(new BigDecimal(0));
084            //set default sequence number.
085            for (PurApAccountingLine account : item.getSourceAccountingLines()) {
086                account.setSequenceNumber(0);
087            }
088        }
089
090        return actionForward;
091    }
092
093    /**
094     * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
095     */
096    @Override
097    protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
098        super.loadDocument(kualiDocumentFormBase);
099        PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) kualiDocumentFormBase;
100        PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) purapForm.getDocument();
101
102        // refresh the account summary (note this also updates the account amounts)
103        purapForm.refreshAccountSummmary();
104
105        for (org.kuali.rice.krad.bo.Note note : (java.util.List<org.kuali.rice.krad.bo.Note>) document.getNotes()) {
106            note.refreshReferenceObject("attachment");
107        }
108
109        // sort the below the line
110        SpringContext.getBean(PurapService.class).sortBelowTheLine(document);
111
112        updateBaseline(document, (PurchasingAccountsPayableFormBase) kualiDocumentFormBase);
113    }
114
115    /**
116     * Updates the baseline accounts on form and doc.
117     *
118     * @param document A descendant of PurchasingAccountsPayableDocument
119     */
120    protected <T extends PurchasingAccountsPayableDocument, V extends KualiAccountingDocumentFormBase> void updateBaseline(T document, V form) {
121        // clear out the old lines first
122        for (PurApItem item : document.getItems()) {
123            // clear out the old lines first
124            item.getBaselineSourceAccountingLines().clear();
125
126            for (PurApAccountingLine sourceAccount : item.getSourceAccountingLines()) {
127                // JHK: KFSMI-287 - removed deep copy since this object will be thrown away after the page renders, we just need a
128                // different path to have them stored on the form
129                // ESPECIALLY since PURAP does not allow lines to be reverted (see calls to setRevertible)
130                item.getBaselineSourceAccountingLines().add(sourceAccount);
131            }
132        }
133    }
134
135    /**
136     * Invokes a service method to refresh the account summary.
137     *
138     * @param mapping  An ActionMapping
139     * @param form     An ActionForm
140     * @param request  The HttpServletRequest
141     * @param response The HttpServletResponse
142     * @return An ActionForward
143     * @throws Exception
144     */
145    public ActionForward refreshAccountSummary(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
146        PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form;
147        PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) purapForm.getDocument();
148        SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(document);
149        purapForm.refreshAccountSummmary();
150        return mapping.findForward(OLEConstants.MAPPING_BASIC);
151    }
152
153    /**
154     * @see org.kuali.ole.sys.web.struts.KualiAccountingDocumentActionBase#uploadAccountingLines(boolean, org.apache.struts.action.ActionForm)
155     */
156    @Override
157    protected void uploadAccountingLines(boolean isSource, ActionForm form) throws FileNotFoundException, IOException {
158        PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form;
159        PurchasingAccountsPayableDocumentBase purapDocument = (PurchasingAccountsPayableDocumentBase) purapForm.getFinancialDocument();
160        PurApAccountingLineParser accountingLineParser = (PurApAccountingLineParser) purapDocument.getAccountingLineParser();
161        List importedLines = null;
162        String errorPathPrefix = PurapConstants.ACCOUNT_DISTRIBUTION_ERROR_KEY;
163        //String errorPathPrefix = "accountDistributionnewSourceLine";
164
165        // import the lines
166        try {
167            FormFile sourceFile = purapForm.getSourceFile();
168            checkUploadFile(sourceFile);
169            GlobalVariables.getMessageMap().clearErrorPath();
170            GlobalVariables.getMessageMap().addToErrorPath(errorPathPrefix);
171            importedLines = accountingLineParser.importSourceAccountingLines(sourceFile.getFileName(), sourceFile.getInputStream(), purapDocument);
172            GlobalVariables.getMessageMap().removeFromErrorPath(errorPathPrefix);
173        } catch (AccountingLineParserException e) {
174            GlobalVariables.getMessageMap().putError(errorPathPrefix, e.getErrorKey(), e.getErrorParameters());
175        }
176
177        // add to list those lines successfully imported
178        if (importedLines != null) {
179            for (Iterator iter = importedLines.iterator(); iter.hasNext(); ) {
180                PurApAccountingLine importedLine = (PurApAccountingLine) iter.next();
181                //boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPathPrefix, purapForm.getDocument(), (AccountingLine) importedLine));
182                //if (rulePassed) {
183                // add accountingLine
184                SpringContext.getBean(PersistenceService.class).retrieveNonKeyFields(importedLine);
185                ((PurchasingFormBase) purapForm).addAccountDistributionsourceAccountingLine(importedLine);
186                //}
187            }
188        }
189    }
190
191    /**
192     * @see org.kuali.ole.sys.web.struts.KualiAccountingDocumentActionBase#insertSourceLine(org.apache.struts.action.ActionMapping,
193     *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
194     */
195    @Override
196    public ActionForward insertSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
197        // It would be preferable to find a way to genericize the KualiAccountingDocument methods but this will work for now
198        PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form;
199
200        // index of item selected
201        int itemIndex = getSelectedLine(request);
202        PurApItem item = null;
203
204        // if custom processing of an accounting line is not done then insert a line generically.
205        if (processCustomInsertAccountingLine(purapForm, request) == false) {
206            String errorPrefix = null;
207            PurApAccountingLine line = null;
208            boolean rulePassed = false;
209            if (itemIndex >= 0) {
210                item = (PurApItem) ((PurchasingAccountsPayableDocument) purapForm.getDocument()).getItem((itemIndex));
211                line = (PurApAccountingLine) ObjectUtils.deepCopy(item.getNewSourceLine());
212                //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line);
213                errorPrefix = OLEPropertyConstants.DOCUMENT + "." + PurapPropertyConstants.ITEM + "[" + Integer.toString(itemIndex) + "]." + OLEConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME;
214                rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPrefix, purapForm.getDocument(), (AccountingLine) line));
215            } else if (itemIndex == -2) {
216                //corrected: itemIndex == -2 is the only case for distribute account
217                //This is the case when we're inserting an accounting line for distribute account.
218                line = ((PurchasingFormBase) purapForm).getAccountDistributionnewSourceLine();
219                //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line);
220                errorPrefix = PurapPropertyConstants.ACCOUNT_DISTRIBUTION_NEW_SRC_LINE;
221                rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPrefix, purapForm.getDocument(), (AccountingLine) line));
222            }
223
224            if (rulePassed) {
225                // add accountingLine
226                SpringContext.getBean(PersistenceService.class).retrieveNonKeyFields(line);
227                if (itemIndex >= 0) {
228                    insertAccountingLine(purapForm, item, line);
229                    // clear the temp account
230                    item.resetAccount();
231                } else if (itemIndex == -2) {
232                    //this is the case for distribute account
233                    ((PurchasingFormBase) purapForm).addAccountDistributionsourceAccountingLine(line);
234                }
235            }
236        }
237
238        return mapping.findForward(OLEConstants.MAPPING_BASIC);
239    }
240
241    /**
242     * Insert the given Accounting Line in several appropriate places in the given item and given form.
243     *
244     * @param financialDocumentForm A form that inherits from PurchasingAccountsPaybleFormBase
245     * @param item                  A PurApItem
246     * @param line                  A PurApAccountingLine
247     */
248    protected void insertAccountingLine(PurchasingAccountsPayableFormBase financialDocumentForm, PurApItem item, PurApAccountingLine line) {
249        PurchasingAccountsPayableDocument preq = (PurchasingAccountsPayableDocument) financialDocumentForm.getDocument();
250
251        Integer index = item.getSourceAccountingLines().size() + 1;
252        line.setSequenceNumber(index);
253        // add it to the item
254        item.getSourceAccountingLines().add(line);
255    }
256
257    /**
258     * Allows the custom processing of an accounting line during a call to insert source line. If a custom method for inserting an
259     * accounting line was performed, then a value of true must be returned.
260     *
261     * @param purapForm
262     * @param request
263     * @return boolean indicating if validation succeeded
264     */
265    public boolean processCustomInsertAccountingLine(PurchasingAccountsPayableFormBase purapForm, HttpServletRequest request) {
266        return false;
267    }
268
269    /**
270     * Insert the given Accounting Line in several appropriate places in the given item and given form.
271     *
272     * @param financialDocumentForm A form that inherits from KualiAccountingDocumentFormBase
273     * @param item                  A PurApItem
274     * @param line                  A PurApAccountingLine
275     */
276    protected void insertAccountingLine(KualiAccountingDocumentFormBase financialDocumentForm, PurApItem item, PurApAccountingLine line) {
277        // add it to the item
278        item.getSourceAccountingLines().add(line);
279    }
280
281    /**
282     * @see org.kuali.ole.sys.web.struts.KualiAccountingDocumentActionBase#deleteSourceLine(org.apache.struts.action.ActionMapping,
283     *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
284     */
285    @Override
286    public ActionForward deleteSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
287        PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form;
288
289        String[] indexes = getSelectedLineForAccounts(request);
290        int itemIndex = Integer.parseInt(indexes[0]);
291        int accountIndex = Integer.parseInt(indexes[1]);
292
293        PurApItem item = (PurApItem) ((PurchasingAccountsPayableDocument) purapForm.getDocument()).getItem((itemIndex));
294        if (itemIndex == -2) {
295            item.getSourceAccountingLines().remove(accountIndex);
296        } else {
297            item.getSourceAccountingLines().remove(accountIndex);
298            List<PurApAccountingLine> purApAccountingLineList = item.getSourceAccountingLines();
299            BigDecimal initialPercent = new BigDecimal(0);
300            for (PurApAccountingLine purApAccountingLine : purApAccountingLineList) {
301                initialPercent = initialPercent.add(purApAccountingLine.getAccountLinePercent());
302            }
303            initialPercent = new BigDecimal(100).subtract(initialPercent);
304            if(initialPercent.intValue()>0){
305                item.resetAccount(initialPercent);
306            }
307            else{
308                item.resetAccount(new BigDecimal(0));
309            }
310        }
311        return mapping.findForward(OLEConstants.MAPPING_BASIC);
312    }
313
314    /**
315     * @see org.kuali.ole.sys.web.struts.KualiAccountingDocumentActionBase#getSourceAccountingLine(org.apache.struts.action.ActionForm,
316     *      javax.servlet.http.HttpServletRequest)
317     */
318    @Override
319    public SourceAccountingLine getSourceAccountingLine(ActionForm form, HttpServletRequest request) {
320        String[] indexes = getSelectedLineForAccounts(request);
321        int itemIndex = Integer.parseInt(indexes[0]);
322        int accountIndex = Integer.parseInt(indexes[1]);
323        PurchasingAccountsPayableFormBase purchasingAccountsPayableForm = (PurchasingAccountsPayableFormBase) form;
324        SourceAccountingLine line;
325        if (itemIndex == -2) {
326            line = customAccountRetrieval(accountIndex, purchasingAccountsPayableForm);
327        } else {
328            PurApItem item = (PurApItem) ((PurchasingAccountsPayableDocument) purchasingAccountsPayableForm.getDocument()).getItem((itemIndex));
329            line = (SourceAccountingLine) ObjectUtils.deepCopy(item.getSourceAccountingLines().get(accountIndex));
330        }
331        return line;
332    }
333
334    /**
335     * Perform custom processing on accounting lines. See <code>getSelectedLineForAccounts</code>.
336     *
337     * @param accountIndex                  The index of the account into the request parameter
338     * @param purchasingAccountsPayableForm A form which inherits from PurchasingAccountsPayableFormBase
339     * @return A SourceAccountingLine
340     */
341    protected SourceAccountingLine customAccountRetrieval(int accountIndex, PurchasingAccountsPayableFormBase purchasingAccountsPayableForm) {
342        // default impl returns null
343        return null;
344    }
345
346    /**
347     * Will return an array of Strings containing 2 indexes, the first String is the item index and the second String is the account
348     * index. These are obtained by parsing the method to call parameter from the request, between the word ".line" and "." The
349     * indexes are separated by a semicolon (:)
350     *
351     * @param request The HttpServletRequest
352     * @return An array of Strings containing pairs of two indices, an item index and a account index
353     */
354    protected String[] getSelectedLineForAccounts(HttpServletRequest request) {
355        String accountString = new String();
356        String parameterName = (String) request.getAttribute(OLEConstants.METHOD_TO_CALL_ATTRIBUTE);
357        if (StringUtils.isNotBlank(parameterName)) {
358            accountString = StringUtils.substringBetween(parameterName, ".line", ".");
359        }
360        String[] result = StringUtils.split(accountString, ":");
361
362        return result;
363    }
364
365    /**
366     * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#downloadBOAttachment(org.apache.struts.action.ActionMapping,
367     *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
368     */
369    @Override
370    public ActionForward downloadBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
371        PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) ((PurchasingAccountsPayableFormBase) form).getDocument();
372
373        for (org.kuali.rice.krad.bo.Note note : (java.util.List<org.kuali.rice.krad.bo.Note>) document.getNotes()) {
374            note.refreshReferenceObject("attachment");
375        }
376
377        return super.downloadBOAttachment(mapping, form, request, response);
378    }
379
380    @Override
381    protected void processAccountingLineOverrides(List accountingLines) {
382        //do nothing purap handles these differently
383    }
384
385    /**
386     * Perform calculation on item line.
387     *
388     * @param mapping  An ActionMapping
389     * @param form     An ActionForm
390     * @param request  The HttpServletRequest
391     * @param response The HttpServletResponse
392     * @return An ActionForward
393     */
394    public ActionForward calculate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
395        return mapping.findForward(OLEConstants.MAPPING_BASIC);
396    }
397
398    public ActionForward clearAllTaxes(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
399        return mapping.findForward(OLEConstants.MAPPING_BASIC);
400    }
401
402    protected void customCalculate(PurchasingAccountsPayableDocument purapDoc) {
403        // do nothing by default
404    }
405
406    /**
407     * Toggles all specific tabs to open
408     *
409     * @param mapping
410     * @param form
411     * @param request
412     * @param response
413     * @return
414     * @throws Exception
415     */
416    public ActionForward showAllAccounts(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
417        KualiForm kualiForm = (KualiForm) form;
418        String accountingLineTab = "AccountingLines";
419        String value = null;
420
421        Map<String, String> tabStates = kualiForm.getTabStates();
422        Map<String, String> newTabStates = new HashMap<String, String>();
423        for (Entry<String, String> tabEntry : tabStates.entrySet()) {
424            if (tabEntry.getKey().startsWith(accountingLineTab)) {
425                newTabStates.put(tabEntry.getKey(), "OPEN");
426            } else {
427                if (tabEntry.getValue() instanceof String) {
428                    value = tabEntry.getValue();
429                } else {
430                    //This is the case where the value is an Array of String,
431                    //so we'll have to get the first element
432                    Object result = tabEntry.getValue();
433                    result.getClass();
434                    value = ((String[]) result)[0];
435                }
436                newTabStates.put(tabEntry.getKey(), value);
437            }
438        }
439        kualiForm.setTabStates(newTabStates);
440        return mapping.findForward(RiceConstants.MAPPING_BASIC);
441    }
442
443    /**
444     * Toggles all specific tabs to closed
445     *
446     * @param mapping
447     * @param form
448     * @param request
449     * @param response
450     * @return
451     * @throws Exception
452     */
453    public ActionForward hideAllAccounts(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
454        KualiForm kualiForm = (KualiForm) form;
455        String accountingLineTab = "AccountingLines";
456        String value = null;
457
458        Map<String, String> tabStates = kualiForm.getTabStates();
459        Map<String, String> newTabStates = new HashMap<String, String>();
460        for (Entry<String, String> tabEntry : tabStates.entrySet()) {
461            if (tabEntry.getKey().startsWith(accountingLineTab)) {
462                newTabStates.put(tabEntry.getKey(), "CLOSE");
463            } else {
464                if (tabEntry.getValue() instanceof String) {
465                    value = tabEntry.getValue();
466                } else {
467                    //This is the case where the value is an Array of String,
468                    //so we'll have to get the first element
469                    Object result = tabEntry.getValue();
470                    result.getClass();
471                    value = ((String[]) result)[0];
472                }
473                newTabStates.put(tabEntry.getKey(), value);
474            }
475        }
476        kualiForm.setTabStates(newTabStates);
477        return mapping.findForward(RiceConstants.MAPPING_BASIC);
478    }
479
480    /**
481     * Override to verify the document has been saved before the note is inserted. This will assure the correct parent object id is
482     * associated with the note.
483     *
484     * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#insertBONote(org.apache.struts.action.ActionMapping,
485     *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
486     */
487    @Override
488    public ActionForward insertBONote(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
489        PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) ((PurchasingAccountsPayableFormBase) form).getDocument();
490        WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
491
492        if (workflowDocument.isInitiated()) {
493            SpringContext.getBean(DocumentService.class).saveDocument(document);
494        }
495
496        return super.insertBONote(mapping, form, request, response);
497    }
498
499}