001/*
002 * Copyright 2006 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.fp.document.web.struts;
017
018import java.io.FileNotFoundException;
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023
024import javax.servlet.http.HttpServletRequest;
025import javax.servlet.http.HttpServletResponse;
026
027import org.apache.struts.action.ActionForm;
028import org.apache.struts.action.ActionForward;
029import org.apache.struts.action.ActionMapping;
030import org.kuali.ole.fp.businessobject.CapitalAccountingLines;
031import org.kuali.ole.fp.businessobject.CapitalAssetAccountsGroupDetails;
032import org.kuali.ole.fp.businessobject.CapitalAssetInformation;
033import org.kuali.ole.fp.businessobject.CapitalAssetInformationDetail;
034import org.kuali.ole.fp.businessobject.options.CapitalAccountingLinesComparator;
035import org.kuali.ole.fp.document.CapitalAccountingLinesDocumentBase;
036import org.kuali.ole.fp.document.validation.event.CapitalAccountingLinesSameObjectCodeSubTypeEvent;
037import org.kuali.ole.integration.cab.CapitalAssetBuilderModuleService;
038import org.kuali.ole.sys.OLEConstants;
039import org.kuali.ole.sys.OLEKeyConstants;
040import org.kuali.ole.sys.businessobject.AccountingLine;
041import org.kuali.ole.sys.businessobject.SourceAccountingLine;
042import org.kuali.ole.sys.businessobject.TargetAccountingLine;
043import org.kuali.ole.sys.context.SpringContext;
044import org.kuali.ole.sys.document.AccountingDocument;
045import org.kuali.ole.sys.web.struts.KualiAccountingDocumentFormBase;
046import org.kuali.rice.core.api.config.property.ConfigurationService;
047import org.kuali.rice.core.api.util.RiceConstants;
048import org.kuali.rice.kew.api.exception.WorkflowException;
049import org.kuali.rice.kns.question.ConfirmationQuestion;
050import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
051import org.kuali.rice.kns.web.struts.form.KualiForm;
052import org.kuali.rice.krad.document.Document;
053import org.kuali.rice.krad.service.KualiRuleService;
054import org.kuali.rice.krad.util.GlobalVariables;
055import org.kuali.rice.krad.util.ObjectUtils;
056
057/**
058 * This is the action class for the CapitalAccountingLinesActionBase.
059 */
060public abstract class CapitalAccountingLinesActionBase extends CapitalAssetInformationActionBase {
061    private CapitalAssetBuilderModuleService capitalAssetBuilderModuleService = SpringContext.getBean(CapitalAssetBuilderModuleService.class);
062
063    /**
064     * removes capitalaccountinglines which is a transient bo.. and call the super method to
065     * create a error correction document.
066     * @see org.kuali.ole.sys.document.web.struts.FinancialSystemTransactionalDocumentActionBase#correct(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
067     */
068    @Override
069    public ActionForward correct(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
070        CapitalAccountingLinesFormBase capitalAccountingLinesFormBase = (CapitalAccountingLinesFormBase) form;
071        CapitalAccountingLinesDocumentBase caldb = (CapitalAccountingLinesDocumentBase) capitalAccountingLinesFormBase.getFinancialDocument();
072        caldb.getCapitalAccountingLines().clear();
073
074        super.correct(mapping, capitalAccountingLinesFormBase, request, response);
075
076        KualiAccountingDocumentFormBase kadfb = (KualiAccountingDocumentFormBase) form;
077        List<CapitalAssetInformation> currentCapitalAssetInformation =  this.getCurrentCapitalAssetInformationObject(kadfb);
078        for (CapitalAssetInformation capitalAsset : currentCapitalAssetInformation) {
079            capitalAsset.setCapitalAssetProcessedIndicator(false);
080            capitalAsset.setCapitalAssetLineAmount(capitalAsset.getCapitalAssetLineAmount().negated());
081            //remove capital asset tag/location asset tag number and serial number as
082            //they will fail because these values will be duplicates.
083            List<CapitalAssetInformationDetail> tagLocationDetails = capitalAsset.getCapitalAssetInformationDetails();
084            for (CapitalAssetInformationDetail tagLocationDetail : tagLocationDetails) {
085                tagLocationDetail.setCapitalAssetTagNumber(null);
086                tagLocationDetail.setCapitalAssetSerialNumber(null);
087            }
088
089            List<CapitalAssetAccountsGroupDetails> groupAccountLines = capitalAsset.getCapitalAssetAccountsGroupDetails();
090            for (CapitalAssetAccountsGroupDetails groupAccountLine : groupAccountLines) {
091                groupAccountLine.setAmount(groupAccountLine.getAmount().negated());
092            }
093        }
094
095        String distributionAmountCode = capitalAccountingLinesFormBase.getCapitalAccountingLine().getDistributionCode();
096
097        AccountingDocument tdoc = (AccountingDocument) capitalAccountingLinesFormBase.getDocument();
098
099        List<CapitalAccountingLines> capitalAccountingLines = new ArrayList();
100        //for every source/target accounting line that has an object code that requires a
101        //capital asset, creates a capital accounting line that the user can select to
102        //perform create or modify asset functions.
103        createCapitalAccountingLines(capitalAccountingLines, tdoc, distributionAmountCode);
104        sortCaptitalAccountingLines(capitalAccountingLines);
105
106        caldb.setCapitalAccountingLines(capitalAccountingLines);
107        //checks capital accounting lines for capital assets and if so checks the select box
108        checkSelectForCapitalAccountingLines(capitalAccountingLinesFormBase);
109
110        checkCapitalAccountingLinesSelected(capitalAccountingLinesFormBase);
111        calculatePercentsForSelectedCapitalAccountingLines(capitalAccountingLinesFormBase);
112
113        //setup the initial next sequence number column..
114        setupIntialNextCapitalAssetLineNumber(capitalAccountingLinesFormBase);
115
116        return mapping.findForward(RiceConstants.MAPPING_BASIC);
117    }
118
119    /**
120     * All document-load operations get routed through here
121     *
122     * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
123     */
124    @Override
125    protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
126        super.loadDocument(kualiDocumentFormBase);
127
128        CapitalAccountingLinesFormBase capitalAccountingLinesFormBase = (CapitalAccountingLinesFormBase) kualiDocumentFormBase;
129        String distributionAmountCode = capitalAccountingLinesFormBase.getCapitalAccountingLine().getDistributionCode();
130
131        AccountingDocument tdoc = (AccountingDocument) kualiDocumentFormBase.getDocument();
132
133        List<CapitalAccountingLines> capitalAccountingLines = new ArrayList();
134        //for every source/target accounting line that has an object code that requires a
135        //capital asset, creates a capital accounting line that the user can select to
136        //perform create or modify asset functions.
137        createCapitalAccountingLines(capitalAccountingLines, tdoc, distributionAmountCode);
138        sortCaptitalAccountingLines(capitalAccountingLines);
139
140        CapitalAccountingLinesDocumentBase caldb = (CapitalAccountingLinesDocumentBase) tdoc;
141        caldb.setCapitalAccountingLines(capitalAccountingLines);
142        //checks capital accounting lines for capital assets and if so checks the select box
143        checkSelectForCapitalAccountingLines(capitalAccountingLinesFormBase);
144
145        checkCapitalAccountingLinesSelected(capitalAccountingLinesFormBase);
146        calculatePercentsForSelectedCapitalAccountingLines(capitalAccountingLinesFormBase);
147
148        //setup the initial next sequence number column..
149        setupIntialNextCapitalAssetLineNumber(kualiDocumentFormBase);
150
151        KualiForm kualiForm = kualiDocumentFormBase;
152        //based on the records in capital accounting lines, capital asset information lists
153        //set the tabs to open if lists not empty else set to close
154        setTabStatesForCapitalAssets(kualiForm);
155    }
156
157    /**
158     *
159     * @see org.kuali.rice.kns.web.struts.action.KualiAction#refresh(org.apache.struts.action.ActionMapping,
160     *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
161     */
162   // @Override
163    @Override
164    public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
165        super.refresh(mapping, form, request, response);
166
167        CapitalAccountingLinesFormBase capitalAccountingLinesFormBase = (CapitalAccountingLinesFormBase) form;
168        CapitalAccountingLinesDocumentBase caldb = (CapitalAccountingLinesDocumentBase) capitalAccountingLinesFormBase.getFinancialDocument();
169
170        List<CapitalAccountingLines> capitalAccountingLines = caldb.getCapitalAccountingLines();
171        KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
172        AccountingDocument tdoc = (AccountingDocument) kualiDocumentFormBase.getDocument();
173
174        capitalAccountingLines = updateCapitalAccountingLines(capitalAccountingLines, tdoc);
175        sortCaptitalAccountingLines(capitalAccountingLines);
176        caldb.setCapitalAccountingLines(capitalAccountingLines);
177
178        //now remove distributed accounting lines if they are not in the capital
179        //accounting lines list.
180
181        KualiAccountingDocumentFormBase kadfb = (KualiAccountingDocumentFormBase) form;
182        List<CapitalAssetInformation> currentCapitalAssetInformation =  this.getCurrentCapitalAssetInformationObject(kadfb);
183
184        removeOrphanDisributedAccountingLines(capitalAccountingLines, currentCapitalAssetInformation);
185        checkCapitalAccountingLinesSelected(capitalAccountingLinesFormBase);
186        setTabStatesForCapitalAssets(form);
187
188        return mapping.findForward(OLEConstants.MAPPING_BASIC);
189    }
190
191    /**
192     * When user adds an accounting line to the either source or target, if the object code on
193     * that line has capital object type code group then a capital accounting line is created.
194     * @see org.kuali.ole.sys.web.struts.KualiAccountingDocumentActionBase#insertAccountingLine(boolean, org.kuali.ole.sys.web.struts.KualiAccountingDocumentFormBase, org.kuali.ole.sys.businessobject.AccountingLine)
195     */
196    @Override
197    protected void insertAccountingLine(boolean isSource, KualiAccountingDocumentFormBase financialDocumentForm, AccountingLine line) {
198        super.insertAccountingLine(isSource, financialDocumentForm, line);
199
200        CapitalAccountingLinesFormBase capitalAccountingLinesFormBase = (CapitalAccountingLinesFormBase) financialDocumentForm;
201        CapitalAccountingLinesDocumentBase caldb = (CapitalAccountingLinesDocumentBase) capitalAccountingLinesFormBase.getFinancialDocument();
202        String distributionAmountCode = capitalAccountingLinesFormBase.getCapitalAccountingLine().getDistributionCode();
203
204        List<CapitalAccountingLines> capitalAccountingLines = caldb.getCapitalAccountingLines();
205
206        //create the corresponding capital accounting line and then
207        //sort the capital accounting lines by object code and account number
208        createCapitalAccountingLine(capitalAccountingLines, line, distributionAmountCode);
209        sortCaptitalAccountingLines(capitalAccountingLines);
210
211        KualiForm kualiForm = financialDocumentForm;
212        //sets the tab states for create/modify capital asset tabs...
213        setTabStatesForCapitalAssets(kualiForm);
214    }
215
216    /**
217     * When user deletes an accounting line to the either source or target, the corresponding line in
218     * capital accounting line is deleted and the distributed accounting line under the
219     * created or modified capital asset line.  When a capital asset does not have any
220     * accounting lines, the capital asset is removed.
221     * @see org.kuali.ole.sys.web.struts.KualiAccountingDocumentActionBase#deleteAccountingLine(boolean, org.kuali.ole.sys.web.struts.KualiAccountingDocumentFormBase, int)
222     */
223    @Override
224    protected void deleteAccountingLine(boolean isSource, KualiAccountingDocumentFormBase financialDocumentForm, int deleteIndex) {
225
226        CapitalAccountingLinesFormBase capitalAccountingLinesFormBase = (CapitalAccountingLinesFormBase) financialDocumentForm;
227        CapitalAccountingLinesDocumentBase caldb = (CapitalAccountingLinesDocumentBase) capitalAccountingLinesFormBase.getFinancialDocument();
228
229        List<CapitalAccountingLines> capitalAccountingLines = caldb.getCapitalAccountingLines();
230
231        if (isSource) {
232            // remove from capital accounting lines for this source line
233            AccountingLine line = (AccountingLine) financialDocumentForm.getFinancialDocument().getSourceAccountingLines().get(deleteIndex);
234            deleteCapitalAccountingLine(capitalAccountingLines, line);
235            deleteCapitalAssetLines(financialDocumentForm, line);
236        }
237        else {
238            // remove from document
239            AccountingLine line = (AccountingLine) financialDocumentForm.getFinancialDocument().getTargetAccountingLines().get(deleteIndex);
240            deleteCapitalAccountingLine(capitalAccountingLines, line);
241            deleteCapitalAssetLines(financialDocumentForm, line);
242        }
243
244        super.deleteAccountingLine(isSource, financialDocumentForm, deleteIndex);
245        sortCaptitalAccountingLines(capitalAccountingLines);
246
247        KualiForm kualiForm = financialDocumentForm;
248
249        //checks capital accounting lines for capital assets and if so checks the select box
250        checkSelectForCapitalAccountingLines(capitalAccountingLinesFormBase);
251        checkCapitalAccountingLinesSelected(capitalAccountingLinesFormBase);
252        calculatePercentsForSelectedCapitalAccountingLines(capitalAccountingLinesFormBase);
253        setTabStatesForCapitalAssets(kualiForm);
254    }
255
256    /**
257     * After uploading the accounting lines, the capital accounting lines will be created from these.
258     * @see org.kuali.ole.sys.web.struts.KualiAccountingDocumentActionBase#uploadAccountingLines(boolean, org.apache.struts.action.ActionForm)
259     */
260    @Override
261    protected void uploadAccountingLines(boolean isSource, ActionForm form) throws FileNotFoundException, IOException {
262        super.uploadAccountingLines(isSource, form);
263
264        KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form;
265        CapitalAccountingLinesFormBase capitalAccountingLinesFormBase = (CapitalAccountingLinesFormBase) form;
266        CapitalAccountingLinesDocumentBase caldb = (CapitalAccountingLinesDocumentBase) capitalAccountingLinesFormBase.getFinancialDocument();
267
268        String distributionAmountCode = capitalAccountingLinesFormBase.getCapitalAccountingLine().getDistributionCode();
269
270        List<CapitalAccountingLines> capitalAccountingLines = caldb.getCapitalAccountingLines();
271        AccountingDocument tdoc = (AccountingDocument) kualiAccountingDocumentFormBase.getDocument();
272
273        createCapitalAccountingLines(capitalAccountingLines, tdoc, distributionAmountCode);
274        sortCaptitalAccountingLines(capitalAccountingLines);
275
276        KualiForm kualiForm = (KualiForm) form;
277        setTabStatesForCapitalAssets(kualiForm);
278    }
279
280    /**
281     * creates the capital accounting lines looking at source and/or target accounting lines.
282     *
283     * @param capitalAccountingLines
284     * @param tdoc
285     * @param distributionAmountCode distribution amount code for the line
286     */
287    protected void createCapitalAccountingLines(List<CapitalAccountingLines> capitalAccountingLines, AccountingDocument tdoc, String distributionAmountCode) {
288        List<SourceAccountingLine> sourceAccountLines = tdoc.getSourceAccountingLines();
289
290        CapitalAccountingLinesDocumentBase caldb = (CapitalAccountingLinesDocumentBase) tdoc;
291
292        for (SourceAccountingLine line : sourceAccountLines) {
293            //create source capital accounting line
294            createCapitalAccountingLine(capitalAccountingLines, line, distributionAmountCode);
295        }
296
297        List<TargetAccountingLine> targetAccountLines = tdoc.getTargetAccountingLines();
298
299        for (TargetAccountingLine line : targetAccountLines) {
300            // create target capital accounting line
301            createCapitalAccountingLine(capitalAccountingLines, line, distributionAmountCode);
302        }
303
304        //sort the capital accounting lines collection
305        sortCaptitalAccountingLines(capitalAccountingLines);
306    }
307
308    /**
309     * updates the capital accounting lines looking at source and/or target accounting lines.
310     *
311     * @param capitalAccountingLines
312     * @param tdoc
313     */
314    protected List<CapitalAccountingLines> updateCapitalAccountingLines(List<CapitalAccountingLines> capitalAccountingLines, AccountingDocument tdoc) {
315        List<SourceAccountingLine> sourceAccountLines = tdoc.getSourceAccountingLines();
316
317        for (SourceAccountingLine line : sourceAccountLines) {
318            //updates source capital accounting line
319            updateCapitalAccountingLine(capitalAccountingLines, line);
320        }
321
322        List<TargetAccountingLine> targetAccountLines = tdoc.getTargetAccountingLines();
323
324        for (TargetAccountingLine line : targetAccountLines) {
325            //updates source capital accounting line
326            updateCapitalAccountingLine(capitalAccountingLines, line);
327        }
328
329        //remove the orphan capital accounting lines that does not have corresponding
330        //accounting line from either source or target.
331       return removeOrphanCapitalAccountingLines(capitalAccountingLines, tdoc);
332    }
333
334    /**
335     * updates the capital accounting lines looking at source and/or target accounting lines.
336     * Removes any capital accounting line that does not have a corresponding
337     * source or target accounting line.
338     * @param capitalAccountingLines
339     * @param tdoc
340     */
341    protected List<CapitalAccountingLines> removeOrphanCapitalAccountingLines(List<CapitalAccountingLines> capitalAccountingLines, AccountingDocument tdoc) {
342        List<CapitalAccountingLines> newCapitalAccountingLines = new ArrayList<CapitalAccountingLines>();
343        if ( capitalAccountingLines != null ) {
344        List<AccountingLine> sourceAccountLines = tdoc.getSourceAccountingLines();
345        for (CapitalAccountingLines capitalAccountingLine : capitalAccountingLines) {
346            if (removeOrphanCapitalAccountingLine(sourceAccountLines, capitalAccountingLine)) {
347                newCapitalAccountingLines.add(capitalAccountingLine);
348            }
349        }
350
351        List<AccountingLine> targetAccountLines = tdoc.getTargetAccountingLines();
352        for (CapitalAccountingLines capitalAccountingLine : capitalAccountingLines) {
353            if (removeOrphanCapitalAccountingLine(targetAccountLines, capitalAccountingLine)) {
354                newCapitalAccountingLines.add(capitalAccountingLine);
355            }
356        }
357        }
358        return newCapitalAccountingLines;
359    }
360
361    /**
362     * If the line exists in capital accounting lines, and that line does not exist in
363     * accounting lines (source or target) then remove the line from capital accounting lines.
364     *
365     * @param capitalAccountingLines
366     * @param line to remove
367     * @return true if the capital accounting line to be removed is found in accounting lines, else false
368     */
369    protected boolean removeOrphanCapitalAccountingLine(List<AccountingLine> accountLines, CapitalAccountingLines capitalAccountingLine) {
370        boolean found = false;
371
372        for (AccountingLine accountingLine : accountLines) {
373            if (capitalAccountingLine.getChartOfAccountsCode().equals(accountingLine.getChartOfAccountsCode()) &&
374                    capitalAccountingLine.getAccountNumber().equals(accountingLine.getAccountNumber()) &&
375                    capitalAccountingLine.getFinancialObjectCode().equals(accountingLine.getFinancialObjectCode()) &&
376                    capitalAccountingLine.getLineType().equalsIgnoreCase(accountingLine instanceof SourceAccountingLine ? OLEConstants.SOURCE : OLEConstants.TARGET)) {
377                found = true;
378            }
379        }
380
381        return found;
382    }
383
384    /**
385     * Method to check for any distributed accounting lines that are not listed in the
386     * capital accounting lines and remove them.
387     *
388     * @param capitalAccountingLines
389     * @param capitalAssets
390     */
391    protected void removeOrphanDisributedAccountingLines(List<CapitalAccountingLines> capitalAccountingLines, List<CapitalAssetInformation> capitalAssets) {
392        List<CapitalAssetInformation> removalCaiList = new ArrayList<CapitalAssetInformation>();
393
394        for (CapitalAssetInformation capitalAsset : capitalAssets) {
395            removeOrphanDisributedAccountingLine(capitalAccountingLines, capitalAsset);
396            if (capitalAsset.getCapitalAssetAccountsGroupDetails().size() == 0) {
397                capitalAsset.getCapitalAssetInformationDetails().clear();
398                removalCaiList.add(capitalAsset);
399            }
400        }
401        //if the removal list is not empty, remove these bunch of capital asset records
402        //for that accounting line.
403        if (ObjectUtils.isNotNull(removalCaiList)) {
404            capitalAssets.removeAll(removalCaiList);
405        }
406    }
407
408    /**
409     * Method to check for any distributed accounting line for a given capital
410     * accounting line that are not listed in the capital assets accounting lines and remove it.
411     *
412     * @param capitalAccountingLines
413     * @param capitalAsset
414     */
415    protected void removeOrphanDisributedAccountingLine(List<CapitalAccountingLines> capitalAccountingLines, CapitalAssetInformation capitalAsset) {
416        List<CapitalAssetAccountsGroupDetails> groupAccountLines = capitalAsset.getCapitalAssetAccountsGroupDetails();
417        CapitalAssetAccountsGroupDetails accountLineToDelete = null;
418
419        for (CapitalAssetAccountsGroupDetails groupAccountLine : groupAccountLines) {
420            if (!checkDistributedAccountingLineExists(capitalAccountingLines, groupAccountLine)) {
421                accountLineToDelete = groupAccountLine;
422                break;
423            }
424        }
425
426        if (ObjectUtils.isNotNull(accountLineToDelete)) {
427            // remove all the group accounting lines so that the asset line can be removed.
428            capitalAsset.getCapitalAssetAccountsGroupDetails().clear();
429        }
430    }
431
432    /**
433     * checks capital accounting lines again the distributed accounting line and if found
434     * return true else false so that this distributed accounting line may be removed.
435     *
436     * @param capitalAccountingLines
437     * @param groupAccountLine
438     * @return true if accounting line exists else return false
439     */
440    protected boolean checkDistributedAccountingLineExists(List<CapitalAccountingLines> capitalAccountingLines, CapitalAssetAccountsGroupDetails groupAccountLine) {
441        boolean exists = false;
442
443        for (CapitalAccountingLines capitalAccountingLine : capitalAccountingLines) {
444            if (groupAccountLine.getSequenceNumber().compareTo(capitalAccountingLine.getSequenceNumber()) == 0 &&
445                    groupAccountLine.getFinancialDocumentLineTypeCode().equals(OLEConstants.SOURCE.equals(capitalAccountingLine.getLineType()) ? OLEConstants.SOURCE_ACCT_LINE_TYPE_CODE : OLEConstants.TARGET_ACCT_LINE_TYPE_CODE) &&
446                    groupAccountLine.getChartOfAccountsCode().equals(capitalAccountingLine.getChartOfAccountsCode()) &&
447                    groupAccountLine.getAccountNumber().equals(capitalAccountingLine.getAccountNumber()) &&
448                    groupAccountLine.getFinancialObjectCode().equals(capitalAccountingLine.getFinancialObjectCode())) {
449                return true;
450            }
451        }
452
453        return false;
454    }
455
456    /**
457     * Checks if the accounting line has an object code that belongs to object sub type group codes and
458     * if so, creates a capital accounting line that will be displayed on the jsp.
459     *
460     * @param capitalAccountingLines
461     * @param line
462     * @param distributionAmountCode
463     * @return List of capitalAccountingLines
464     */
465    protected List<CapitalAccountingLines> createCapitalAccountingLine(List<CapitalAccountingLines> capitalAccountingLines, AccountingLine line, String distributionAmountCode) {
466        Integer sequenceNumber = capitalAccountingLines.size() + 1;
467
468        if (capitalAssetBuilderModuleService.hasCapitalAssetObjectSubType(line)) {
469            //capital object code so we need to build the capital accounting line...
470            CapitalAccountingLines cal = addCapitalAccountingLine(capitalAccountingLines, line);
471            cal.setDistributionAmountCode(distributionAmountCode);
472            capitalAccountingLines.add(cal);
473        }
474
475        return capitalAccountingLines;
476    }
477
478    /**
479     * Checks if the accounting line exits in the capital accounting lines
480     * and if so, updates the other information.
481     * else inserts a new record into the collection
482     *
483     * @param capitalAccountingLines
484     * @param line
485     * @return List of capitalAccountingLines
486     */
487    protected void updateCapitalAccountingLine(List<CapitalAccountingLines> capitalAccountingLines, AccountingLine line) {
488        boolean found = false;
489
490        for (CapitalAccountingLines capitalAccountingLine : capitalAccountingLines) {
491            if (capitalAccountingLine.getChartOfAccountsCode().equals(line.getChartOfAccountsCode()) &&
492                    capitalAccountingLine.getAccountNumber().equals(line.getAccountNumber()) &&
493                    capitalAccountingLine.getFinancialObjectCode().equals(line.getFinancialObjectCode()) &&
494                    capitalAccountingLine.getLineType().equalsIgnoreCase(line instanceof SourceAccountingLine ? OLEConstants.SOURCE : OLEConstants.TARGET) &&
495                    capitalAccountingLine.getSequenceNumber().compareTo(line.getSequenceNumber()) == 0) {
496                capitalAccountingLine.setFinancialSubObjectCode(line.getFinancialSubObjectCode());
497                capitalAccountingLine.setSubAccountNumber(line.getSubAccountNumber());
498                capitalAccountingLine.setProjectCode(line.getProjectCode());
499                capitalAccountingLine.setOrganizationReferenceId(line.getOrganizationReferenceId());
500                capitalAccountingLine.setAmount(line.getAmount());
501                capitalAccountingLine.setFinancialDocumentLineDescription(line.getFinancialDocumentLineDescription());
502                found = true;
503                break;
504            }
505        }
506
507        if (!found) {
508            //capital object code so we need to build the capital accounting line...
509            if (capitalAssetBuilderModuleService.hasCapitalAssetObjectSubType(line)) {
510                CapitalAccountingLines cal = addCapitalAccountingLine(capitalAccountingLines, line);
511                capitalAccountingLines.add(cal);
512            }
513        }
514    }
515
516    /**
517     * convenience method to add a new capital accounting line to the collection of capital
518     * accounting lines.
519     *
520     * @param capitalAccountingLines
521     * @param line
522     * @return cal newly created capital accounting line
523     */
524    protected CapitalAccountingLines addCapitalAccountingLine(List<CapitalAccountingLines> capitalAccountingLines, AccountingLine line) {
525        CapitalAccountingLines cal = new CapitalAccountingLines();
526
527        if (line instanceof SourceAccountingLine) {
528            cal.setLineType(OLEConstants.SOURCE);
529        }
530        else {
531            cal.setLineType(OLEConstants.TARGET);
532        }
533        cal.setSequenceNumber(line.getSequenceNumber());
534        cal.setChartOfAccountsCode(line.getChartOfAccountsCode());
535        cal.setAccountNumber(line.getAccountNumber());
536        cal.setSubAccountNumber(line.getSubAccountNumber());
537        cal.setFinancialObjectCode(line.getFinancialObjectCode());
538        cal.setFinancialSubObjectCode(line.getFinancialSubObjectCode());
539        cal.setProjectCode(line.getProjectCode());
540        cal.setOrganizationReferenceId(line.getOrganizationReferenceId());
541        cal.setFinancialDocumentLineDescription(line.getFinancialDocumentLineDescription());
542        cal.setAmount(line.getAmount());
543        cal.setAccountLinePercent(null);
544        cal.setSelectLine(false);
545
546        return cal;
547    }
548
549    /**
550     * If the line exists in capital accounting lines, that will be deleted.
551     *
552     * @param capitalAccountingLines
553     * @param line to remove
554     * @return List of capitalAccountingLines
555     */
556    protected List<CapitalAccountingLines> deleteCapitalAccountingLine(List<CapitalAccountingLines> capitalAccountingLines, AccountingLine line) {
557        CapitalAccountingLines cal = null;
558
559        for (CapitalAccountingLines capitalAccountingLine : capitalAccountingLines) {
560            if (capitalAccountingLine.getChartOfAccountsCode().equals(line.getChartOfAccountsCode()) &&
561                    capitalAccountingLine.getAccountNumber().equals(line.getAccountNumber()) &&
562                    capitalAccountingLine.getFinancialObjectCode().equals(line.getFinancialObjectCode()) &&
563                    capitalAccountingLine.getLineType().equalsIgnoreCase(line instanceof SourceAccountingLine ? OLEConstants.SOURCE : OLEConstants.TARGET)) {
564                cal = capitalAccountingLine;
565                break;
566            }
567        }
568
569        if (ObjectUtils.isNotNull(cal)) {
570            capitalAccountingLines.remove(cal);
571        }
572
573        return capitalAccountingLines;
574    }
575
576    /**
577     * sort the capital accounting lines collection based on financial object code and account number.
578     * @param capitalAccountingLines List of capital accounting lines
579     */
580    protected void sortCaptitalAccountingLines(List<CapitalAccountingLines> capitalAccountingLines) {
581        CapitalAccountingLinesComparator calComparator = new CapitalAccountingLinesComparator();
582
583        //sort the collection based on financial object code and account number
584        Collections.sort(capitalAccountingLines, calComparator);
585    }
586
587    /**
588     * Action "create" creates assets for the selected capital
589     * accounting lines.  An error message is shown if no capital accounting lines
590     * are selected for processing.  The action checks if the selected
591     * capital accounting lines object sub type cross based on the system paramter
592     * values for the object subtypes and if so prompts the user to confirm to continue
593     * to create the assets for the select capital accounting lines.
594     *
595     * @param mapping
596     * @param form
597     * @param request
598     * @param response
599     * @return ActionForward
600     * @throws Exception
601     */
602    public ActionForward createAsset(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
603        CapitalAccountingLinesFormBase calfb = (CapitalAccountingLinesFormBase) form;
604        String distributionAmountCode = calfb.getCapitalAccountingLine().getDistributionCode();
605        if (OLEConstants.CapitalAssets.DISTRIBUTE_COST_EQUALLY_CODE.equals(distributionAmountCode)) {
606           calfb.setDistributeEqualAmount(true);
607        }
608        else {
609            calfb.setDistributeEqualAmount(false);
610        }
611
612        boolean createAction = calfb.getCapitalAccountingLine().isCanCreateAsset();
613        calfb.setEditCreateOrModify(false);
614
615        GlobalVariables.getMessageMap().clearErrorMessages();
616        if (!capitalAccountingLinesSelected(calfb)) {
617            GlobalVariables.getMessageMap().putError(OLEConstants.EDIT_ACCOUNTING_LINES_FOR_CAPITALIZATION_ERRORS, OLEKeyConstants.ERROR_DOCUMENT_ACCOUNTING_LINE_FOR_CAPITALIZATAION_REQUIRED_CREATE);
618            return mapping.findForward(OLEConstants.MAPPING_BASIC);
619        }
620        else {
621            calfb.setEditCreateOrModify(false);
622        }
623
624        Document document = calfb.getFinancialDocument();
625
626        //if same object subtypes then continue creating capital assets....
627        if (checkObjecSubTypeCrossingCapitalAccountingLines(document)) {
628            //question the user if to continue....
629            ActionForward forward = performQuestionPrompt(mapping, form, request, response, OLEConstants.CapitalAssets.CAPITAL_ASSET_CREATE_ACTION_INDICATOR, distributionAmountCode);
630            if (forward != null) {
631                return forward;
632            }
633        }
634        else {
635            createCapitalAssetsForSelectedAccountingLines(form , calfb, OLEConstants.CapitalAssets.CAPITAL_ASSET_CREATE_ACTION_INDICATOR, distributionAmountCode);
636        }
637
638        //restore the tab states....
639        setTabStatesForCapitalAssets(form);
640
641        return mapping.findForward(OLEConstants.MAPPING_BASIC);
642    }
643
644    /**
645     * Action "modify" creates assets for the selected capital
646     * accounting lines.  An error message is shown if no capital accounting lines
647     * are selected for processing.  The action checks if the selected
648     * capital accounting lines object sub type cross based on the system paramter
649     * values for the object subtypes and if so prompts the user to confirm to continue
650     * to create the assets for the select capital accounting lines.
651     *
652     * @param mapping
653     * @param form
654     * @param request
655     * @param response
656     * @return ActionForward
657     * @throws Exception
658     */
659    public ActionForward modifyAsset(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
660        CapitalAccountingLinesFormBase calfb = (CapitalAccountingLinesFormBase) form;
661        String distributionAmountCode = calfb.getCapitalAccountingLine().getDistributionCode();
662
663        if (OLEConstants.CapitalAssets.DISTRIBUTE_COST_EQUALLY_CODE.equals(distributionAmountCode)) {
664            calfb.setDistributeEqualAmount(true);
665         }
666        else {
667            calfb.setDistributeEqualAmount(false);
668        }
669
670        GlobalVariables.getMessageMap().clearErrorMessages();
671        if (!capitalAccountingLinesSelected(calfb)) {
672            GlobalVariables.getMessageMap().putError(OLEConstants.EDIT_ACCOUNTING_LINES_FOR_CAPITALIZATION_ERRORS, OLEKeyConstants.ERROR_DOCUMENT_ACCOUNTING_LINE_FOR_CAPITALIZATAION_REQUIRED_MODIFY);
673            return mapping.findForward(OLEConstants.MAPPING_BASIC);
674        }
675        else {
676            calfb.setEditCreateOrModify(false);
677        }
678
679        Document document = calfb.getFinancialDocument();
680
681        //if same object subtypes then continue creating capital assets....
682        if (checkObjecSubTypeCrossingCapitalAccountingLines(document)) {
683            //question the user if to continue....
684            ActionForward forward = performQuestionPrompt(mapping, form, request, response, OLEConstants.CapitalAssets.CAPITAL_ASSET_MODIFY_ACTION_INDICATOR, distributionAmountCode);
685            if (forward != null) {
686                return forward;
687            }
688        }
689        else {
690            createCapitalAssetsForSelectedAccountingLines(form , calfb, OLEConstants.CapitalAssets.CAPITAL_ASSET_MODIFY_ACTION_INDICATOR, distributionAmountCode);
691        }
692
693        //restore the tab states....
694        setTabStatesForCapitalAssets(form);
695
696        return mapping.findForward(OLEConstants.MAPPING_BASIC);
697    }
698
699    /**
700     * Helper method to first calculate the percents for the selected capital accounting lines as
701     * the percent is required if the user is distributing the amounts by individual amount
702     * method.  It then populates created or modified assets with asset accounting lines.
703     *
704     * @param form
705     * @param calfb
706     * @param actionTypeIndicator indicates whether creating an asset for "create" or "modify" actions.
707     * @param distributionAmountCode amount distribution code
708     */
709    protected void createCapitalAssetsForSelectedAccountingLines(ActionForm form, CapitalAccountingLinesFormBase calfb, String actionTypeIndicator, String distributionAmountCode) {
710        calculatePercentsForSelectedCapitalAccountingLines(calfb);
711        createCapitalAssetForGroupAccountingLines(calfb, actionTypeIndicator, distributionAmountCode);
712            checkCapitalAccountingLinesSelected(calfb);
713
714            KualiForm kualiForm = (KualiForm) form;
715            setTabStatesForCapitalAssets(kualiForm);
716        }
717
718
719    /**
720     * checks the capital accounting lines if any of the lines have been selected for
721     * further processing.
722     *
723     * @param calfb
724     * @return selected return true if lines have been selected else return false
725     */
726    protected boolean capitalAccountingLinesSelected(CapitalAccountingLinesFormBase calfb) {
727        boolean selected = false;
728
729        CapitalAccountingLinesDocumentBase caldb = (CapitalAccountingLinesDocumentBase) calfb.getFinancialDocument();
730        List<CapitalAccountingLines> capitalAccountingLines = caldb.getCapitalAccountingLines();
731
732        for (CapitalAccountingLines capitalAccountingLine : capitalAccountingLines) {
733            if (capitalAccountingLine.isSelectLine()) {
734                selected = true;
735                break;
736            }
737        }
738
739        return selected;
740    }
741
742   /**
743    * runs the validation to check if object subtypes crosses groups on
744    * selected capital accounting lines.
745    *
746    * @param form
747    * @return true if rule passed else false
748    */
749    protected boolean checkObjecSubTypeCrossingCapitalAccountingLines(Document document) {
750        boolean differentObjecSubtypes = true;
751        differentObjecSubtypes &= getRuleService().applyRules(new CapitalAccountingLinesSameObjectCodeSubTypeEvent(document));
752
753        return differentObjecSubtypes;
754    }
755
756    /**
757     *
758     * @param mapping An ActionMapping
759     * @param form An ActionForm
760     * @param request The HttpServletRequest
761     * @param response The HttpServletResponse
762     * @throws Exception
763     * @param distributionAmountCode
764     * @return An ActionForward
765     */
766    protected ActionForward performQuestionPrompt(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String actionTypeCode, String distributionAmountCode) throws Exception {
767        ActionForward forward = null;
768        Object question = request.getParameter(OLEConstants.QUESTION_INST_ATTRIBUTE_NAME);
769        CapitalAccountingLinesFormBase calfb = (CapitalAccountingLinesFormBase) form;
770
771        if (question == null) {
772            String questionText = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(OLEKeyConstants.WARNING_NOT_SAME_OBJECT_SUB_TYPES);
773            return this.performQuestionWithoutInput(mapping, form, request, response, OLEConstants.OBJECT_SUB_TYPES_DIFFERENT_QUESTION, questionText, OLEConstants.CONFIRMATION_QUESTION, OLEConstants.ROUTE_METHOD, "");
774        }
775        else {
776            Object buttonClicked = request.getParameter(OLEConstants.QUESTION_CLICKED_BUTTON);
777            // If the user replies 'Yes' the question, proceed..
778            if (OLEConstants.OBJECT_SUB_TYPES_DIFFERENT_QUESTION.equals(question) && ConfirmationQuestion.YES.equals(buttonClicked)) {
779                createCapitalAssetsForSelectedAccountingLines(form , calfb, actionTypeCode, distributionAmountCode);
780                
781                KualiForm kualiForm = (KualiForm) form;
782                setTabStatesForCapitalAssets(kualiForm);
783
784                return mapping.findForward(OLEConstants.MAPPING_BASIC);
785
786            }
787            // If the user replies 'No' to either of the questions
788            else {
789                uncheckCapitalAccountingLinesSelected(calfb);
790                forward = mapping.findForward(OLEConstants.MAPPING_BASIC);
791            }
792        }
793
794        return forward;
795    }
796
797    /**
798     * Get the rule service
799     *
800     * @return ruleService
801     */
802    protected KualiRuleService getRuleService() {
803        return SpringContext.getBean(KualiRuleService.class);
804    }
805}