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.sys.document;
017    
018    import java.util.ArrayList;
019    import java.util.List;
020    
021    import org.apache.commons.lang.StringUtils;
022    import org.kuali.kfs.gl.service.SufficientFundsService;
023    import org.kuali.kfs.sys.KFSConstants;
024    import org.kuali.kfs.sys.KFSKeyConstants;
025    import org.kuali.kfs.sys.businessobject.Bank;
026    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
027    import org.kuali.kfs.sys.businessobject.SufficientFundsItem;
028    import org.kuali.kfs.sys.context.SpringContext;
029    import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
030    import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
031    import org.kuali.rice.kew.exception.WorkflowException;
032    import org.kuali.rice.kns.exception.ValidationException;
033    import org.kuali.rice.kns.rule.event.ApproveDocumentEvent;
034    import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
035    import org.kuali.rice.kns.rule.event.RouteDocumentEvent;
036    import org.kuali.rice.kns.service.ParameterService;
037    import org.kuali.rice.kns.util.GlobalVariables;
038    
039    /**
040     * Base implementation for a general ledger posting document.
041     */
042    public class GeneralLedgerPostingDocumentBase extends LedgerPostingDocumentBase implements GeneralLedgerPostingDocument {
043        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(GeneralLedgerPostingDocumentBase.class);
044    
045        protected List<GeneralLedgerPendingEntry> generalLedgerPendingEntries;
046    
047        /**
048         * Default constructor.
049         */
050        public GeneralLedgerPostingDocumentBase() {
051            super();
052            setGeneralLedgerPendingEntries(new ArrayList<GeneralLedgerPendingEntry>());
053        }
054    
055        /**
056         * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocument#getGeneralLedgerPendingEntries()
057         */
058        public List<GeneralLedgerPendingEntry> getGeneralLedgerPendingEntries() {
059            return generalLedgerPendingEntries;
060        }
061    
062        /**
063         * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocument#getGeneralLedgerPendingEntry(int)
064         */
065        public GeneralLedgerPendingEntry getGeneralLedgerPendingEntry(int index) {
066            while (generalLedgerPendingEntries.size() <= index) {
067                generalLedgerPendingEntries.add(new GeneralLedgerPendingEntry());
068            }
069            return generalLedgerPendingEntries.get(index);
070        }
071    
072        /**
073         * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocument#setGeneralLedgerPendingEntries(java.util.List)
074         */
075        public void setGeneralLedgerPendingEntries(List<GeneralLedgerPendingEntry> generalLedgerPendingEntries) {
076            this.generalLedgerPendingEntries = generalLedgerPendingEntries;
077        }
078    
079        /**
080         * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocument#checkSufficientFunds()
081         */
082        public List<SufficientFundsItem> checkSufficientFunds() {
083            LOG.debug("checkSufficientFunds() started");
084    
085            if (documentPerformsSufficientFundsCheck()) {
086                SufficientFundsService sufficientFundsService = SpringContext.getBean(SufficientFundsService.class);
087                return sufficientFundsService.checkSufficientFunds(this);
088            }
089            else {
090                return new ArrayList<SufficientFundsItem>();
091            }
092        }
093    
094        /**
095         * This method checks to see if SF checking should be done for this document. This was originally part of
096         * SufficientFundsService.checkSufficientFunds() but was externalized so documents that need to override any of the SF methods
097         * can still explicitly check this
098         * 
099         * @return
100         */
101        public boolean documentPerformsSufficientFundsCheck() {
102            // check for reversing entries generated by an error correction.
103            return StringUtils.isBlank(this.getDocumentHeader().getFinancialDocumentInErrorNumber());
104        }
105    
106        /**
107         * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocument#getPendingLedgerEntriesForSufficientFundsChecking()
108         */
109        public List<GeneralLedgerPendingEntry> getPendingLedgerEntriesForSufficientFundsChecking() {
110            return getGeneralLedgerPendingEntries();
111        }
112    
113        /**
114         * Override to call super and then iterate over all GLPEs and update the approved code appropriately.
115         * 
116         * @see Document#doRouteStatusChange()
117         */
118        @Override
119        public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
120            super.doRouteStatusChange(statusChangeEvent);
121            if (getDocumentHeader().getWorkflowDocument().stateIsProcessed()) {
122                changeGeneralLedgerPendingEntriesApprovedStatusCode(); // update all glpes for doc and set their status to approved
123            }
124            else if (getDocumentHeader().getWorkflowDocument().stateIsCanceled() || getDocumentHeader().getWorkflowDocument().stateIsDisapproved()) {
125                removeGeneralLedgerPendingEntries();
126                if (this instanceof ElectronicPaymentClaiming) {
127                    ((ElectronicPaymentClaiming)this).declaimElectronicPaymentClaims();
128                }
129            }
130        }
131    
132        /**
133         * This method iterates over all of the GLPEs for a document and sets their approved status code to APPROVED "A".
134         */
135        protected void changeGeneralLedgerPendingEntriesApprovedStatusCode() {
136            for (GeneralLedgerPendingEntry glpe : getGeneralLedgerPendingEntries()) {
137                glpe.setFinancialDocumentApprovedCode(KFSConstants.DocumentStatusCodes.APPROVED);
138            }
139        }
140    
141        /**
142         * This method calls the service to remove all of the GLPE's associated with this document
143         */
144        protected void removeGeneralLedgerPendingEntries() {
145            GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
146            glpeService.delete(getDocumentHeader().getDocumentNumber());
147        }
148    
149        /**
150         * @see org.kuali.rice.kns.document.DocumentBase#toCopy()
151         */
152        @Override
153        public void toCopy() throws WorkflowException {
154            super.toCopy();
155            getGeneralLedgerPendingEntries().clear();
156        }
157    
158        /**
159         * @see org.kuali.rice.kns.document.TransactionalDocumentBase#toErrorCorrection()
160         */
161        @Override
162        public void toErrorCorrection() throws WorkflowException {
163            super.toErrorCorrection();
164            getGeneralLedgerPendingEntries().clear();
165        }
166    
167        @Override
168        public void prepareForSave(KualiDocumentEvent event) {
169            super.prepareForSave(event);
170            // TODO - add KFS wrappers of Rice Events to list
171            if (event instanceof RouteDocumentEvent || event instanceof ApproveDocumentEvent) {
172                // generate general ledger pending entries should be called prior to sufficient funds checking
173                List<SufficientFundsItem> sfItems = checkSufficientFunds();
174                if (!sfItems.isEmpty()) {
175                    for (SufficientFundsItem sfItem : sfItems) {
176                        GlobalVariables.getMessageMap().putError(KFSConstants.ACCOUNTING_LINE_ERRORS, KFSKeyConstants.SufficientFunds.ERROR_INSUFFICIENT_FUNDS, new String[] { sfItem.getAccount().getChartOfAccountsCode(), sfItem.getAccount().getAccountNumber(), StringUtils.isNotBlank(sfItem.getSufficientFundsObjectCode()) ? sfItem.getSufficientFundsObjectCode() : KFSConstants.NOT_AVAILABLE_STRING, sfItem.getAccountSufficientFundsCode() });
177                    }
178                    throw new ValidationException("Insufficient Funds on this Document:");
179                }
180            }
181        }
182        
183        /**
184         * Adds a GeneralLedgerPendingEntry to this document's list of pending entries
185         * @param pendingEntry a pending entry to add
186         */
187        public void addPendingEntry(GeneralLedgerPendingEntry pendingEntry) {
188            generalLedgerPendingEntries.add(pendingEntry);
189        }
190        
191        /**
192         * This resets this document's list of general ledger pending etnries, though it does not delete those entries (however, the GeneralLedgerPendingEntryService will in most cases when this method is called).
193         */
194        public void clearAnyGeneralLedgerPendingEntries() {
195            generalLedgerPendingEntries = new ArrayList<GeneralLedgerPendingEntry>();
196        }
197    }