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.gl.batch.service.impl;
017    
018    import java.util.Date;
019    
020    import org.apache.ojb.broker.metadata.MetadataManager;
021    import org.kuali.kfs.gl.GeneralLedgerConstants;
022    import org.kuali.kfs.gl.batch.service.AccountingCycleCachingService;
023    import org.kuali.kfs.gl.batch.service.PostTransaction;
024    import org.kuali.kfs.gl.businessobject.Reversal;
025    import org.kuali.kfs.gl.businessobject.SufficientFundBalances;
026    import org.kuali.kfs.gl.businessobject.Transaction;
027    import org.kuali.kfs.gl.dataaccess.SufficientFundBalancesDao;
028    import org.kuali.kfs.sys.KFSConstants;
029    import org.kuali.kfs.sys.service.ReportWriterService;
030    import org.kuali.rice.kns.service.PersistenceStructureService;
031    import org.kuali.rice.kns.util.KualiDecimal;
032    import org.kuali.rice.kns.util.ObjectUtils;
033    import org.springframework.transaction.annotation.Transactional;
034    
035    /**
036     * An implementation of PostTransaction which posts a transaction to the appropriate sufficient funds record
037     */
038    @Transactional
039    public class PostSufficientFundBalances implements PostTransaction {
040        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PostSufficientFundBalances.class);
041        
042        private AccountingCycleCachingService accountingCycleCachingService;
043        private PersistenceStructureService persistenceStructureService;
044        
045        /**
046         * Constructs a PostSufficientFundBalances instance
047         */
048        public PostSufficientFundBalances() {
049            super();
050        }
051    
052        /**
053         * Posts the transaction to the appropriate sufficient funds records
054         * 
055         * @param t the transaction which is being posted
056         * @param mode the mode the poster is currently running in
057         * @param postDate the date this transaction should post to
058         * @param posterReportWriterService the writer service where the poster is writing its report
059         * @return the accomplished post type
060         * @see org.kuali.kfs.gl.batch.service.PostTransaction#post(org.kuali.kfs.gl.businessobject.Transaction, int, java.util.Date)
061         */
062        public String post(Transaction t, int mode, Date postDate, ReportWriterService posterReportWriterService) {
063            LOG.debug("post() started");
064    
065            String returnCode = GeneralLedgerConstants.UPDATE_CODE;
066    
067            if (KFSConstants.SF_TYPE_NO_CHECKING.equals(t.getAccount().getAccountSufficientFundsCode())) {
068                // Don't need to post
069                return GeneralLedgerConstants.EMPTY_CODE;
070            }
071    
072            // Get the Sufficient funds code
073            // Sufficient Funds Code
074            String sufficientFundsObjectCode = null;
075            if (KFSConstants.SF_TYPE_OBJECT.equals(t.getAccount().getAccountSufficientFundsCode())) {
076                sufficientFundsObjectCode = t.getFinancialObjectCode();
077            }
078            else if (KFSConstants.SF_TYPE_LEVEL.equals(t.getAccount().getAccountSufficientFundsCode())) {
079                if (ObjectUtils.isNull(t.getFinancialObject())) {
080                    return "E:Could not find sufficient funds object code for " + t.toString();
081                }
082                sufficientFundsObjectCode = t.getFinancialObject().getFinancialObjectLevelCode();
083            }
084            else if (KFSConstants.SF_TYPE_CONSOLIDATION.equals(t.getAccount().getAccountSufficientFundsCode())) {
085                //sufficientFundsObjectCode = t.getFinancialObject().getFinancialObjectLevel().getFinancialConsolidationObjectCode();
086                if (ObjectUtils.isNull(t.getFinancialObject())) {
087                    return "E:Could not find sufficient funds object code for " + t.toString();
088                }
089                sufficientFundsObjectCode = accountingCycleCachingService.getObjectLevel(t.getFinancialObject().getChartOfAccountsCode(), t.getFinancialObject().getFinancialObjectLevelCode()).getFinancialConsolidationObjectCode();
090            }
091            else if (KFSConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(t.getAccount().getAccountSufficientFundsCode()) || KFSConstants.SF_TYPE_ACCOUNT.equals(t.getAccount().getAccountSufficientFundsCode())) {
092                sufficientFundsObjectCode = GeneralLedgerConstants.getSpaceFinancialObjectCode();
093            }
094            else {
095                return "E:Invalid sufficient funds code (" + t.getAccount().getAccountSufficientFundsCode() + ")";
096            }
097    
098            // Look to see if there is a sufficient funds record for this
099            SufficientFundBalances sfBalance = accountingCycleCachingService.getSufficientFundBalances(t.getUniversityFiscalYear(), t.getChartOfAccountsCode(), t.getAccountNumber(), sufficientFundsObjectCode);
100            if (sfBalance == null) {
101                returnCode = GeneralLedgerConstants.INSERT_CODE;
102                sfBalance = new SufficientFundBalances();
103                sfBalance.setUniversityFiscalYear(t.getUniversityFiscalYear());
104                sfBalance.setChartOfAccountsCode(t.getChartOfAccountsCode());
105                sfBalance.setAccountNumber(t.getAccountNumber());
106                sfBalance.setFinancialObjectCode(sufficientFundsObjectCode);
107                sfBalance.setAccountActualExpenditureAmt(KualiDecimal.ZERO);
108                sfBalance.setAccountEncumbranceAmount(KualiDecimal.ZERO);
109                sfBalance.setCurrentBudgetBalanceAmount(KualiDecimal.ZERO);
110                sfBalance.setAccountSufficientFundsCode(t.getAccount().getAccountSufficientFundsCode());
111            }
112    
113            if (KFSConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(t.getAccount().getAccountSufficientFundsCode())) {
114                // 2640-PROCESS-CASH
115                if (t.getFinancialBalanceTypeCode().equals(t.getOption().getActualFinancialBalanceTypeCd())) {
116                    if (t.getFinancialObjectCode().equals(t.getChart().getFinancialCashObjectCode()) || t.getFinancialObjectCode().equals(t.getChart().getFinAccountsPayableObjectCode())) {
117                        // 2641-PROCESS-CASH-ACTUAL
118                        updateBudgetAmount(t.getTransactionDebitCreditCode(), sfBalance, t.getTransactionLedgerEntryAmount());
119                    }
120                    else {
121                        // No need to post this
122                        return GeneralLedgerConstants.EMPTY_CODE;
123                    }
124                }
125                else if (t.getFinancialBalanceTypeCode().equals(t.getOption().getExtrnlEncumFinBalanceTypCd()) || t.getFinancialBalanceTypeCode().equals(t.getOption().getIntrnlEncumFinBalanceTypCd()) || t.getFinancialBalanceTypeCode().equals(t.getOption().getPreencumbranceFinBalTypeCd()) || t.getOption().getCostShareEncumbranceBalanceTypeCd().equals(t.getFinancialBalanceTypeCode())) {
126                    if (t.getFinancialObjectTypeCode().equals(t.getOption().getFinObjTypeExpenditureexpCd()) || t.getFinancialObjectTypeCode().equals(t.getOption().getFinObjTypeExpendNotExpCode()) || t.getOption().getFinancialObjectTypeTransferExpenseCd().equals(t.getFinancialObjectTypeCode()) || t.getOption().getFinObjTypeExpNotExpendCode().equals(t.getFinancialObjectTypeCode())) {
127                        // 2462-PROCESS-CASH-ENCUMBRANCE
128                        updateEncumbranceAmount(t.getTransactionDebitCreditCode(), sfBalance, t.getTransactionLedgerEntryAmount());
129                    }
130                    else {
131                        // No need to post this
132                        return GeneralLedgerConstants.EMPTY_CODE;
133                    }
134                }
135                else {
136                    // No need to post this
137                    return GeneralLedgerConstants.EMPTY_CODE;
138                }
139            }
140            else {
141                // 2630-PROCESS-OBJECT-OR-ACCOUNT
142                if (t.getFinancialObjectTypeCode().equals(t.getOption().getFinObjTypeExpenditureexpCd()) || t.getFinancialObjectTypeCode().equals(t.getOption().getFinObjTypeExpendNotExpCode()) || t.getOption().getFinancialObjectTypeTransferExpenseCd().equals(t.getFinancialObjectTypeCode()) || t.getOption().getFinObjTypeExpNotExpendCode().equals(t.getFinancialObjectTypeCode())) {
143                    if (t.getFinancialBalanceTypeCode().equals(t.getOption().getActualFinancialBalanceTypeCd())) {
144                        // 2631-PROCESS-OBJTACCT-ACTUAL
145                        updateExpendedAmount(t.getTransactionDebitCreditCode(), sfBalance, t.getTransactionLedgerEntryAmount());
146                    }
147                    else if (t.getFinancialBalanceTypeCode().equals(t.getOption().getExtrnlEncumFinBalanceTypCd()) || t.getFinancialBalanceTypeCode().equals(t.getOption().getIntrnlEncumFinBalanceTypCd()) || t.getFinancialBalanceTypeCode().equals(t.getOption().getPreencumbranceFinBalTypeCd()) || t.getFinancialBalanceTypeCode().equals(t.getOption().getCostShareEncumbranceBalanceTypeCd())) {
148                        // 2632-PROCESS-OBJTACCT-ENCMBRNC
149                        updateEncumbranceAmount(t.getTransactionDebitCreditCode(), sfBalance, t.getTransactionLedgerEntryAmount());
150                    }
151                    else if (t.getFinancialBalanceTypeCode().equals(t.getOption().getBudgetCheckingBalanceTypeCd())) {
152                        sfBalance.setCurrentBudgetBalanceAmount(sfBalance.getCurrentBudgetBalanceAmount().add(t.getTransactionLedgerEntryAmount()));
153                    }
154                    else {
155                        // No need to post this
156                        return GeneralLedgerConstants.EMPTY_CODE;
157                    }
158                }
159                else {
160                    // No need to post this
161                    return GeneralLedgerConstants.EMPTY_CODE;
162                }
163            }
164    
165            // If we get here, we need to save the balance entry
166            if (returnCode.equals(GeneralLedgerConstants.INSERT_CODE)) {
167                accountingCycleCachingService.insertSufficientFundBalances(sfBalance);
168            } else {
169                accountingCycleCachingService.updateSufficientFundBalances(sfBalance);
170            }
171    
172    
173            return returnCode;
174        }
175    
176        /**
177         * Updates the expenditure amount of a given sufficient funds balance record
178         * 
179         * @param debitCreditCode whether the the amount should be debited or credited to the SF balance
180         * @param bal a sufficient funds balance to update
181         * @param amount the amount to debit or credit
182         */
183        protected void updateExpendedAmount(String debitCreditCode, SufficientFundBalances bal, KualiDecimal amount) {
184            if (KFSConstants.GL_CREDIT_CODE.equals(debitCreditCode)) {
185                bal.setAccountActualExpenditureAmt(bal.getAccountActualExpenditureAmt().subtract(amount));
186            }
187            else if (KFSConstants.GL_DEBIT_CODE.equals(debitCreditCode) || KFSConstants.GL_BUDGET_CODE.equals(debitCreditCode)) {
188                bal.setAccountActualExpenditureAmt(bal.getAccountActualExpenditureAmt().add(amount));
189            }
190        }
191    
192        /**
193         * Updates the encumbrance amount of a given sufficient funds balance record
194         * 
195         * @param debitCreditCode whether the the amount should be debited or credited to the SF balance
196         * @param bal a sufficient funds balance to update
197         * @param amount the amount to debit or credit
198         */
199        protected void updateEncumbranceAmount(String debitCreditCode, SufficientFundBalances bal, KualiDecimal amount) {
200            if (KFSConstants.GL_CREDIT_CODE.equals(debitCreditCode)) {
201                bal.setAccountEncumbranceAmount(bal.getAccountEncumbranceAmount().subtract(amount));
202            }
203            else if (KFSConstants.GL_DEBIT_CODE.equals(debitCreditCode) || KFSConstants.GL_BUDGET_CODE.equals(debitCreditCode)) {
204                bal.setAccountEncumbranceAmount(bal.getAccountEncumbranceAmount().add(amount));
205            }
206        }
207    
208        /**
209         * Updates the budget amount of a given sufficient funds balance record
210         * 
211         * @param debitCreditCode whether the the amount should be debited or credited to the SF balance
212         * @param bal a sufficient funds balance to update
213         * @param amount the amount to debit or credit
214         */
215        protected void updateBudgetAmount(String debitCreditCode, SufficientFundBalances bal, KualiDecimal amount) {
216            if (KFSConstants.GL_CREDIT_CODE.equals(debitCreditCode)) {
217                bal.setCurrentBudgetBalanceAmount(bal.getCurrentBudgetBalanceAmount().subtract(amount));
218            }
219            else if (KFSConstants.GL_DEBIT_CODE.equals(debitCreditCode) || KFSConstants.GL_BUDGET_CODE.equals(debitCreditCode)) {
220                bal.setCurrentBudgetBalanceAmount(bal.getCurrentBudgetBalanceAmount().add(amount));
221            }
222        }
223    
224        /**
225         * @see org.kuali.kfs.gl.batch.service.PostTransaction#getDestinationName()
226         */
227        public String getDestinationName() {
228            return persistenceStructureService.getTableName(SufficientFundBalances.class);
229        }
230        
231        public void setAccountingCycleCachingService(AccountingCycleCachingService accountingCycleCachingService) {
232            this.accountingCycleCachingService = accountingCycleCachingService;
233        }
234    
235        public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
236            this.persistenceStructureService = persistenceStructureService;
237        }
238    }