001/*
002 * Copyright 2009 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.gl.batch.service.impl;
017
018import java.sql.Date;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025import org.apache.commons.lang.ArrayUtils;
026import org.kuali.ole.coa.businessobject.A21SubAccount;
027import org.kuali.ole.coa.businessobject.BalanceType;
028import org.kuali.ole.coa.businessobject.ObjectCode;
029import org.kuali.ole.coa.businessobject.OffsetDefinition;
030import org.kuali.ole.coa.businessobject.PriorYearAccount;
031import org.kuali.ole.coa.businessobject.SubFundGroup;
032import org.kuali.ole.coa.businessobject.SubObjectCode;
033import org.kuali.ole.coa.service.A21SubAccountService;
034import org.kuali.ole.coa.service.ObjectCodeService;
035import org.kuali.ole.coa.service.ObjectTypeService;
036import org.kuali.ole.coa.service.OffsetDefinitionService;
037import org.kuali.ole.coa.service.SubFundGroupService;
038import org.kuali.ole.coa.service.SubObjectCodeService;
039import org.kuali.ole.gl.GeneralLedgerConstants;
040import org.kuali.ole.gl.batch.EncumbranceForwardStep;
041import org.kuali.ole.gl.batch.ScrubberStep;
042import org.kuali.ole.gl.batch.service.AccountingCycleCachingService;
043import org.kuali.ole.gl.batch.service.EncumbranceClosingOriginEntryGenerationService;
044import org.kuali.ole.gl.batch.service.impl.exception.FatalErrorException;
045import org.kuali.ole.gl.businessobject.Encumbrance;
046import org.kuali.ole.gl.businessobject.OriginEntryFull;
047import org.kuali.ole.select.document.OlePurchaseOrderDocument;
048import org.kuali.ole.sys.OLEConstants;
049import org.kuali.ole.sys.OLEPropertyConstants;
050import org.kuali.ole.sys.context.SpringContext;
051import org.kuali.ole.sys.service.FlexibleOffsetAccountService;
052import org.kuali.ole.sys.service.OptionsService;
053import org.kuali.ole.sys.service.impl.OleParameterConstants;
054import org.kuali.rice.core.api.parameter.ParameterEvaluator;
055import org.kuali.rice.core.api.parameter.ParameterEvaluatorService;
056import org.kuali.rice.core.api.util.type.KualiDecimal;
057import org.kuali.rice.coreservice.framework.parameter.ParameterService;
058import org.kuali.rice.kns.service.DataDictionaryService;
059import org.kuali.rice.krad.service.BusinessObjectService;
060import org.kuali.rice.krad.service.KRADServiceLocator;
061
062/**
063 * The default implementation of the EncumbranceClosingOriginEntryGenerationService
064 */
065public class EncumbranceClosingOriginEntryGenerationServiceImpl implements EncumbranceClosingOriginEntryGenerationService {
066    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EncumbranceClosingOriginEntryGenerationServiceImpl.class);
067    protected ParameterService parameterService;
068    protected OffsetDefinitionService offsetDefinitionService;
069    protected ObjectCodeService objectCodeService;
070    protected DataDictionaryService dataDictionaryService;
071    protected FlexibleOffsetAccountService flexibleOffsetAccountService;
072    protected A21SubAccountService a21SubAccountService;
073    protected SubObjectCodeService subObjectCodeService;
074    protected OptionsService optionsService;
075    protected SubFundGroupService subFundGroupService;
076    protected BusinessObjectService businessObjectService;
077    protected AccountingCycleCachingService accountingCycleCachingService;
078
079    /**
080     * @see org.kuali.ole.gl.batch.service.EncumbranceClosingOriginEntryGenerationService#createBeginningBalanceEntryOffsetPair(org.kuali.ole.gl.businessobject.Encumbrance, java.lang.Integer, java.sql.Date)
081     */
082    @Override
083    public OriginEntryOffsetPair createCostShareBeginningBalanceEntryOffsetPair(Encumbrance encumbrance, Date transactionDate) {
084        final String GL_ACLO = getParameterService().getParameterValueAsString(OleParameterConstants.GENERAL_LEDGER_BATCH.class, OLEConstants.SystemGroupParameterNames.GL_ANNUAL_CLOSING_DOC_TYPE);
085        final String GL_ORIGINATION_CODE = getParameterService().getParameterValueAsString(OleParameterConstants.GENERAL_LEDGER_BATCH.class, OLEConstants.SystemGroupParameterNames.GL_ORIGINATION_CODE);
086
087        OriginEntryOffsetPair pair = new OriginEntryOffsetPair();
088
089        // Generate the entry ...
090
091        OriginEntryFull entry = new OriginEntryFull(encumbrance.getDocumentTypeCode(), encumbrance.getOriginCode());
092
093        String description = encumbrance.getTransactionEncumbranceDescription();
094        String fromDesc = "FR-" + encumbrance.getChartOfAccountsCode() + encumbrance.getAccountNumber();
095        int descLength = getDataDictionaryService().getAttributeMaxLength(OriginEntryFull.class, OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_DESC);
096        if ((description.length() + fromDesc.length()) < descLength) {
097            int padLength = descLength - (description.length() + fromDesc.length());
098            StringBuilder sb = new StringBuilder();
099            for (int i = 0; i < padLength; i++) {
100                sb.append(' ');
101            }
102            sb.append(fromDesc);
103            fromDesc = sb.toString();
104            description += fromDesc;
105        }
106        else if ((description.length() + fromDesc.length()) > descLength) {
107            description = description.substring(0, (descLength - fromDesc.length())) + fromDesc;
108        }
109        else {
110            description += fromDesc;
111        }
112        entry.setTransactionLedgerEntryDescription(description);
113
114        // SpringContext is used because this method is static.
115        A21SubAccount a21SubAccount = getA21SubAccountService().getByPrimaryKey(encumbrance.getChartOfAccountsCode(), encumbrance.getAccountNumber(), encumbrance.getSubAccountNumber());
116
117        entry.setUniversityFiscalYear(new Integer(encumbrance.getUniversityFiscalYear().intValue() + 1));
118        entry.setChartOfAccountsCode(a21SubAccount.getCostShareChartOfAccountCode());
119        entry.setAccountNumber(a21SubAccount.getCostShareSourceAccountNumber());
120        entry.setSubAccountNumber(a21SubAccount.getCostShareSourceSubAccountNumber());
121
122        // The subAccountNumber is set to dashes in the OriginEntryFull constructor.
123        if (entry.getSubAccountNumber() == null || OLEConstants.EMPTY_STRING.equals(entry.getSubAccountNumber().trim())) {
124            entry.setSubAccountNumber(OLEConstants.getDashSubAccountNumber());
125        }
126
127//        ObjectCode finObjCode = accountingCycleCachingService.getObjectCode(encumbrance.getUniversityFiscalYear(), entry.getChartOfAccountsCode(), entry.getFinancialObjectCode());
128//        if (finObjCode != null)
129//            entry.setFinancialObjectTypeCode(finObjCode.getFinancialObjectTypeCode());
130//        
131        
132        ObjectCode encumbranceObjectCode = accountingCycleCachingService.getObjectCode(entry.getUniversityFiscalYear(), entry.getChartOfAccountsCode(), encumbrance.getObjectCode());
133        
134        if (null != encumbranceObjectCode) {
135
136            String financialObjectLevelCode = encumbranceObjectCode.getFinancialObjectLevelCode();
137            String financialObjectCode = encumbrance.getObjectCode();
138            
139            String overriddenObjectCode = overrideCostShareObjectCode(financialObjectLevelCode, financialObjectCode);
140            final ObjectCode overriddenObject = this.getAccountingCycleCachingService().getObjectCode(entry.getUniversityFiscalYear(), entry.getChartOfAccountsCode(), overriddenObjectCode);
141            
142            String param = parameterService.getSubParameterValueAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.COST_SHARE_OBJECT_CODE_BY_LEVEL_PARM_NM, overriddenObject.getFinancialObjectLevelCode());
143            if (param == null) {
144                param = parameterService.getSubParameterValueAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.COST_SHARE_OBJECT_CODE_BY_LEVEL_PARM_NM, "DEFAULT");
145                if (param == null) {
146                    throw new RuntimeException("Unable to determine cost sharing object code from object level (" + overriddenObject.getFinancialObjectLevelCode() + ").  Default entry missing.");
147                }
148            }
149            financialObjectCode = param;
150
151         // Lookup the new object code
152            ObjectCode newObjectCode = accountingCycleCachingService.getObjectCode(entry.getUniversityFiscalYear(), entry.getChartOfAccountsCode(), financialObjectCode);
153            if (newObjectCode != null) {
154                entry.setFinancialObjectTypeCode(newObjectCode.getFinancialObjectTypeCode());
155                entry.setFinancialObjectCode(financialObjectCode);
156            }
157            else {
158                LOG.error("Error retrieving ObjectCode("+entry.getUniversityFiscalYear()+"/"+entry.getChartOfAccountsCode()+"/"+financialObjectCode+")");
159                pair.setFatalErrorFlag(true);
160                return pair;
161            }
162        } else {
163
164            LOG.error("Error retrieving ObjectCode("+entry.getUniversityFiscalYear()+"/"+entry.getChartOfAccountsCode()+"/"+entry.getFinancialObjectCode()+")");
165            pair.setFatalErrorFlag(true);
166            return pair;
167
168        }
169        
170        
171        entry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
172        entry.setFinancialBalanceTypeCode(OLEConstants.BALANCE_TYPE_COST_SHARE_ENCUMBRANCE);
173
174        entry.setUniversityFiscalPeriodCode(OLEConstants.PERIOD_CODE_BEGINNING_BALANCE);
175        entry.setTransactionLedgerEntrySequenceNumber(new Integer(0));
176        entry.setDocumentNumber(encumbrance.getDocumentNumber());
177        entry.setFinancialBalanceTypeCode(OLEConstants.BALANCE_TYPE_COST_SHARE_ENCUMBRANCE);
178
179        KualiDecimal delta = encumbrance.getAccountLineEncumbranceAmount().subtract(encumbrance.getAccountLineEncumbranceClosedAmount());
180        if (delta.isPositive()) {
181            entry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
182            entry.setTransactionLedgerEntryAmount(delta);
183        }
184        else {
185            entry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
186            entry.setTransactionLedgerEntryAmount(delta.negated());
187        }
188        entry.setTransactionEncumbranceUpdateCode(OLEConstants.ENCUMB_UPDT_DOCUMENT_CD);
189        entry.setProjectCode(OLEConstants.getDashProjectCode());
190        entry.setTransactionDate(transactionDate);
191
192        pair.setEntry(entry);
193
194        // And now the offset ...
195
196        OriginEntryFull offset = new OriginEntryFull(encumbrance.getDocumentTypeCode(), encumbrance.getOriginCode());
197        final String GENERATED_TRANSACTION_LEDGER_ENTRY_DESCRIPTION = getParameterService().getParameterValueAsString(EncumbranceForwardStep.class, GeneralLedgerConstants.EncumbranceClosingOriginEntry.GENERATED_TRANSACTION_LEDGER_ENTRY_DESCRIPTION);
198        offset.setTransactionLedgerEntryDescription(GENERATED_TRANSACTION_LEDGER_ENTRY_DESCRIPTION);
199
200        offset.setUniversityFiscalYear(new Integer(encumbrance.getUniversityFiscalYear().intValue() + 1));
201        offset.setChartOfAccountsCode(a21SubAccount.getCostShareChartOfAccountCode());
202        offset.setAccountNumber(a21SubAccount.getCostShareSourceAccountNumber());
203        offset.setSubAccountNumber(a21SubAccount.getCostShareSourceSubAccountNumber());
204        if (offset.getSubAccountNumber() == null || OLEConstants.EMPTY_STRING.equals(offset.getSubAccountNumber().trim())) {
205            offset.setSubAccountNumber(OLEConstants.getDashSubAccountNumber());
206        }
207        // Lookup the offset definition for the explicit entry we just created.
208        OffsetDefinition offsetDefinition = getOffsetDefinitionService().getByPrimaryId(entry.getUniversityFiscalYear(), entry.getChartOfAccountsCode(), entry.getFinancialDocumentTypeCode(), entry.getFinancialBalanceTypeCode());
209        // Set values from the offset definition if it was found.
210        if (null != offsetDefinition) {
211
212            offset.setFinancialObjectCode(offsetDefinition.getFinancialObjectCode());
213            offset.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
214        }
215        else { // Log an exception if the offset definition was not found.
216
217            LOG.info("FATAL ERROR: One of the following errors occurred (no way to know exactly which):\n\t" + "- OFFSET DEFINITION NOT FOUND\n\t" + "- ERROR ACCESSING OFSD TABLE");
218            pair.setFatalErrorFlag(true);
219            return pair;
220
221        }
222        offset.setFinancialBalanceTypeCode(OLEConstants.BALANCE_TYPE_COST_SHARE_ENCUMBRANCE);
223        // Validate the object code for the explicit entry.
224        ObjectCode objectCode = getObjectCodeService().getByPrimaryId(offset.getUniversityFiscalYear(), offset.getChartOfAccountsCode(), offset.getFinancialObjectCode());
225        if (null != objectCode) {
226            offset.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode());
227        }
228        else {
229            LOG.info("FATAL ERROR: One of the following errors occurred (no way to know exactly which):\n\t" + "- NO OBJECT FOR OBJECT ON OFSD\n\t" + "- ERROR ACCESSING OBJECT TABLE");
230            pair.setFatalErrorFlag(true);
231            return pair;
232        }
233        offset.setUniversityFiscalPeriodCode(OLEConstants.PERIOD_CODE_BEGINNING_BALANCE);
234        offset.setDocumentNumber(encumbrance.getDocumentNumber());
235        offset.setTransactionLedgerEntrySequenceNumber(new Integer(0));
236        if (delta.isPositive()) {
237            offset.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
238            offset.setTransactionLedgerEntryAmount(delta);
239        }
240        else {
241            offset.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
242            offset.setTransactionLedgerEntryAmount(delta.negated());
243        }
244
245        offset.setTransactionEncumbranceUpdateCode(null);
246        offset.setOrganizationDocumentNumber(null);
247        offset.setProjectCode(OLEConstants.getDashProjectCode());
248        offset.setTransactionDate(transactionDate);
249        offset.setOrganizationReferenceId(null);
250        offset.setReferenceFinancialDocumentTypeCode(null);
251        offset.setReferenceFinancialSystemOriginationCode(null);
252        offset.setReferenceFinancialDocumentNumber(null);
253        offset.setReversalDate(null);
254        
255        getFlexibleOffsetAccountService().updateOffset(offset);
256
257        pair.setOffset(offset);
258
259        return pair;
260    }
261
262    /**
263     * @see org.kuali.ole.gl.batch.service.EncumbranceClosingOriginEntryGenerationService#createCostShareBeginningBalanceEntryOffsetPair(org.kuali.ole.gl.businessobject.Encumbrance, java.sql.Date)
264     */
265    @Override
266    public OriginEntryOffsetPair createBeginningBalanceEntryOffsetPair(Encumbrance encumbrance, Integer closingFiscalYear, Date transactionDate) {
267        OriginEntryOffsetPair pair = new OriginEntryOffsetPair();
268
269        // Build the entry ...
270        OriginEntryFull entry = new OriginEntryFull(encumbrance.getDocumentTypeCode(), encumbrance.getOriginCode());
271
272        Integer thisFiscalYear = new Integer(closingFiscalYear.intValue() + 1);
273        entry.setUniversityFiscalYear(thisFiscalYear);
274        entry.setChartOfAccountsCode(encumbrance.getChartOfAccountsCode());
275        entry.setAccountNumber(encumbrance.getAccountNumber());
276        entry.setSubAccountNumber(encumbrance.getSubAccountNumber());
277
278        ObjectCode objectCode = accountingCycleCachingService.getObjectCode(entry.getUniversityFiscalYear(), entry.getChartOfAccountsCode(), encumbrance.getObjectCode());
279        
280        if (null != objectCode) {
281
282            entry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode());
283
284            if (null != objectCode.getNextYearFinancialObjectCode() && !OLEConstants.EMPTY_STRING.equals(objectCode.getNextYearFinancialObjectCode().trim())) {
285
286                entry.setFinancialObjectCode(objectCode.getNextYearFinancialObjectCode());
287
288            }
289            else {
290
291                entry.setFinancialObjectCode(encumbrance.getObjectCode());
292
293            }
294
295        }
296        
297        
298        else {
299
300            LOG.error("Error retrieving ObjectCode("+entry.getUniversityFiscalYear()+"/"+entry.getChartOfAccountsCode()+"/"+entry.getFinancialObjectCode()+")");
301            pair.setFatalErrorFlag(true);
302            return pair;
303
304        }
305
306        SubObjectCode subObjectCode = getSubObjectCodeService().getByPrimaryId(encumbrance.getUniversityFiscalYear(), encumbrance.getChartOfAccountsCode(), encumbrance.getAccountNumber(), encumbrance.getObjectCode(), encumbrance.getSubObjectCode());
307
308        if (null != subObjectCode) {
309
310            entry.setFinancialSubObjectCode(subObjectCode.getFinancialSubObjectCode());
311
312        }
313        else {
314
315            entry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
316
317        }
318
319        entry.setFinancialBalanceTypeCode(encumbrance.getBalanceTypeCode());
320        entry.setUniversityFiscalPeriodCode(OLEConstants.PERIOD_CODE_BEGINNING_BALANCE);
321        Map map = new HashMap();
322        map.put(OLEConstants.PUR_AP_IDEN,encumbrance.getDocumentNumber());
323        List<OlePurchaseOrderDocument> olePurchaseOrderDocument = ( List<OlePurchaseOrderDocument>)KRADServiceLocator.getBusinessObjectService().findMatching(OlePurchaseOrderDocument.class, map);
324        if(olePurchaseOrderDocument.size() > 0){
325            entry.setDocumentNumber(olePurchaseOrderDocument.get(0).getDocumentNumber());
326        }
327        entry.setTransactionLedgerEntrySequenceNumber(new Integer(1));
328        entry.setTransactionLedgerEntryDescription(encumbrance.getTransactionEncumbranceDescription());
329        entry.setTransactionLedgerEntryAmount(encumbrance.getAccountLineEncumbranceAmount().subtract(encumbrance.getAccountLineEncumbranceClosedAmount()));
330
331        if (entry.getTransactionLedgerEntryAmount().isNegative()) {
332
333            entry.setTransactionLedgerEntryAmount(entry.getTransactionLedgerEntryAmount().negated());
334            entry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
335
336        }
337        else {
338
339            entry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
340
341        }
342
343        entry.setTransactionDate(transactionDate);
344        entry.setOrganizationDocumentNumber(null);
345        entry.setProjectCode(OLEConstants.getDashProjectCode());
346        entry.setOrganizationReferenceId(null);
347        entry.setReferenceFinancialDocumentTypeCode(null);
348        entry.setReferenceFinancialSystemOriginationCode(null);
349        entry.setReferenceFinancialDocumentNumber(null);
350        entry.setReversalDate(null);
351        entry.setTransactionEncumbranceUpdateCode(OLEConstants.ENCUMB_UPDT_DOCUMENT_CD);
352
353        pair.setEntry(entry);
354
355        final String OBJECT_CODE_FOR_BALANCE_TYPE_INTERNAL_ENCUMBRANCE = getParameterService().getParameterValueAsString(EncumbranceForwardStep.class, GeneralLedgerConstants.EncumbranceClosingOriginEntry.OFFSET_OBJECT_CODE_FOR_INTERNAL_ENCUMBRANCE);
356        final String OBJECT_CODE_FOR_BALANCE_TYPE_PRE_ENCUMBRANCE = getParameterService().getParameterValueAsString(EncumbranceForwardStep.class, GeneralLedgerConstants.EncumbranceClosingOriginEntry.OFFSET_OBJECT_CODE_FOR_PRE_ENCUMBRANCE);
357        final String OBJECT_CODE_FOR_BALANCE_TYPE_EXTERNAL_ENCUMBRANCE = getParameterService().getParameterValueAsString(EncumbranceForwardStep.class, GeneralLedgerConstants.EncumbranceClosingOriginEntry.OFFSET_OBJECT_CODE_FOR_EXTERNAL_ENCUMBRANCE);
358        final String BEGINNING_FUND_TRANSACTION_LEDGER_ENTRY_DESCRIPTION = getParameterService().getParameterValueAsString(EncumbranceForwardStep.class, GeneralLedgerConstants.EncumbranceClosingOriginEntry.BEGINNING_FUND_BALANCE_TRANSACTION_LEDGER_ENTRY_DESCRIPTION);
359
360        // And now build the offset.
361        OriginEntryFull offset = new OriginEntryFull(entry);
362        offset.setTransactionLedgerEntryAmount(entry.getTransactionLedgerEntryAmount());
363        // OLEConstants.BALANCE_TYPE_INTERNAL_ENCUMBRANCE case...
364        offset.setFinancialObjectCode(OBJECT_CODE_FOR_BALANCE_TYPE_INTERNAL_ENCUMBRANCE);
365
366        if (OLEConstants.BALANCE_TYPE_PRE_ENCUMBRANCE.equals(entry.getFinancialBalanceTypeCode())) {
367
368            offset.setFinancialObjectCode(OBJECT_CODE_FOR_BALANCE_TYPE_PRE_ENCUMBRANCE);
369
370        }
371        else if (OLEConstants.BALANCE_TYPE_EXTERNAL_ENCUMBRANCE.equals(entry.getFinancialBalanceTypeCode())) {
372
373            offset.setFinancialObjectCode(OBJECT_CODE_FOR_BALANCE_TYPE_EXTERNAL_ENCUMBRANCE);
374
375        }
376
377        offset.setFinancialObjectTypeCode(getOptionsService().getCurrentYearOptions().getFinObjectTypeFundBalanceCd());
378        offset.setTransactionLedgerEntryDescription(BEGINNING_FUND_TRANSACTION_LEDGER_ENTRY_DESCRIPTION);
379
380        if (OLEConstants.GL_DEBIT_CODE.equals(entry.getTransactionDebitCreditCode())) {
381
382            offset.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
383
384        }
385        else {
386
387            offset.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
388
389        }
390        getFlexibleOffsetAccountService().updateOffset(offset);
391        
392        pair.setOffset(offset);
393
394        return pair;
395    }
396    
397    /**
398     * Determine whether or not an encumbrance should be carried forward from one fiscal year to the next.
399     * 
400     * @param encumbrance the encumbrance to qualify
401     * @return true if the encumbrance should be rolled forward from the closing fiscal year to the opening fiscal year.
402     */
403    @Override
404    public boolean shouldForwardEncumbrance(Encumbrance encumbrance) {
405        // null guard
406        if (null == encumbrance) {
407            return false;
408        }
409
410        if (encumbrance.getAccountLineEncumbranceAmount().equals(encumbrance.getAccountLineEncumbranceClosedAmount())) {
411            return false;
412        }
413
414        if (getEncumbranceBalanceTypeCodes().contains(encumbrance.getBalanceTypeCode())) {
415
416            ParameterEvaluator evaluator = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(EncumbranceForwardStep.class, GeneralLedgerConstants.EncumbranceClosingOriginEntry.FORWARD_ENCUMBRANCE_BALANCE_TYPE_AND_ORIGIN_CODE,encumbrance.getBalanceTypeCode(),  encumbrance.getOriginCode());
417            if (!evaluator.evaluationSucceeds()) {
418                return false;
419            }
420            else if (OLEConstants.BALANCE_TYPE_PRE_ENCUMBRANCE.equals(encumbrance.getBalanceTypeCode())) {
421                // pre-encumbrances are forwarded, but only if they're related to contracts and grants accounts
422                PriorYearAccount priorYearAccount = retrievePriorYearAccount(encumbrance.getChartOfAccountsCode(), encumbrance.getAccountNumber());
423                // the account on the encumbrance must be valid
424                if (null == priorYearAccount) {
425                    LOG.info("No prior year account for chart \"" + encumbrance.getChartOfAccountsCode() + "\" and account \"" + encumbrance.getAccountNumber() + "\"");
426                    return false;
427                }
428                // the sub fund group must exist for the prior year account and the
429                // encumbrance must not be closed.
430                return priorYearAccount.isForContractsAndGrants();
431            }
432            else {
433                // we're still here? because we're an external encumbrance, and we always get forwarded
434                return true;
435            }
436        }
437        // we're still here? because we're not of a valid encumbrance balance type; we don't get forwarded
438        return false;
439
440    }
441    
442    /**
443     * @return a list of BalanceType codes which correspond to encumbrance balance types
444     */
445    protected List<String> getEncumbranceBalanceTypeCodes() {
446        List<String> balanceTypeCodes = new ArrayList<String>();
447        
448        
449        Map<String, Object> keys = new HashMap<String, Object>();
450        keys.put("active", Boolean.TRUE);
451        keys.put("finBalanceTypeEncumIndicator", Boolean.TRUE);
452        Collection balanceTypes = businessObjectService.findMatching(BalanceType.class, keys);
453        for (Object balanceTypeAsObject : balanceTypes) {
454            ParameterEvaluator evaluator = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(EncumbranceForwardStep.class, GeneralLedgerConstants.EncumbranceClosingOriginEntry.FORWARDING_ENCUMBRANCE_BALANCE_TYPES, ((BalanceType)balanceTypeAsObject).getCode());
455            if (evaluator.evaluationSucceeds())
456                balanceTypeCodes.add(((BalanceType)balanceTypeAsObject).getCode()); 
457        }
458       
459        return balanceTypeCodes;
460    }
461    
462    /**
463     * Determine whether or not the encumbrance has been fully relieved.
464     * 
465     * @param encumbrance the encumbrance to qualify
466     * @return true if the amount closed on the encumbrance is NOT equal to the amount of the encumbrance itself, e.g. if the
467     *         encumbrance has not yet been paid off.
468     */
469    @Override
470    public boolean isEncumbranceClosed(Encumbrance encumbrance) {
471        if (encumbrance.getAccountLineEncumbranceAmount().doubleValue() == encumbrance.getAccountLineEncumbranceClosedAmount().doubleValue()) {
472            return false;
473        }
474        return true;
475    }
476    
477    /**
478     * Do some validation and make sure that the encumbrance A21SubAccount is a cost share sub-account.
479     * 
480     * @param entry not used in this implementation
481     * @param offset not used in this implementation
482     * @param encumbrance the encumbrance whose A21SubAccount must be qualified
483     * @param objectTypeCode the object type code of the generated entries
484     * @return true if the encumbrance is eligible for cost share.
485     * @throws FatalErrorException thrown if a given A21SubAccount, SubFundGroup, or PriorYearAccount record is not found in the database
486     */
487    @Override
488    public boolean shouldForwardCostShareForEncumbrance(OriginEntryFull entry, OriginEntryFull offset, Encumbrance encumbrance, String objectTypeCode) throws FatalErrorException {
489        PriorYearAccount priorYearAccount = retrievePriorYearAccount(encumbrance.getChartOfAccountsCode(), encumbrance.getAccountNumber());
490
491        // the sub fund group for the prior year account must exist.
492        String subFundGroupCode = null;
493        if (null != priorYearAccount) {
494            subFundGroupCode = priorYearAccount.getSubFundGroupCode();
495        }
496        else {
497            // this message was carried over from the cobol.
498            throw new FatalErrorException("ERROR ACCESSING PRIOR YR ACCT TABLE FOR " + encumbrance.getAccountNumber());
499        }
500
501        SubFundGroup subFundGroup = getSubFundGroupService().getByPrimaryId(subFundGroupCode);
502        if (null != subFundGroup) {
503            if (!priorYearAccount.isForContractsAndGrants()) {
504                return false;
505            }
506        }
507        else {
508            throw new FatalErrorException("ERROR ACCESSING SUB FUND GROUP TABLE FOR " + subFundGroupCode);
509        }
510
511        // I think this is redundant to the statement a few lines above here.
512        // In any case, the sub fund group must not be contracts and grants.
513        if (!priorYearAccount.isForContractsAndGrants()) {
514            return false;
515        }
516
517        ObjectTypeService objectTypeService = (ObjectTypeService) SpringContext.getBean(ObjectTypeService.class);
518        List<String> expenseObjectCodeTypes = objectTypeService.getCurrentYearExpenseObjectTypes();
519
520        String[] encumbranceBalanceTypeCodes = new String[] { OLEConstants.BALANCE_TYPE_EXTERNAL_ENCUMBRANCE, OLEConstants.BALANCE_TYPE_INTERNAL_ENCUMBRANCE, OLEConstants.BALANCE_TYPE_PRE_ENCUMBRANCE };
521
522        // the object type code must be an expense and the encumbrance balance type code must correspond to an internal, external or
523        // pre-encumbrance
524        if (!expenseObjectCodeTypes.contains(objectTypeCode) || !ArrayUtils.contains(encumbranceBalanceTypeCodes, encumbrance.getBalanceTypeCode())) {
525            return false;
526        }
527        else if (!encumbrance.getSubAccountNumber().equals(OLEConstants.getDashSubAccountNumber())) {
528            A21SubAccount a21SubAccount = getA21SubAccountService().getByPrimaryKey(encumbrance.getChartOfAccountsCode(), encumbrance.getAccountNumber(), encumbrance.getSubAccountNumber());
529            if (null == a21SubAccount) {
530                // Error message carried over from cobol. not very well descriptive.
531                // Just indicates that the a21 sub account doesn't exist.
532                throw new FatalErrorException("ERROR ACCESSING A21 SUB ACCOUNT TABLE FOR ENCUMBRANCE " + encumbrance.getChartOfAccountsCode() + "-" + encumbrance.getAccountNumber() + " " + encumbrance.getSubAccountNumber());
533            }
534            // everything is valid, return true if the a21 sub account is a cost share sub-account
535            return OLEConstants.SubAccountType.COST_SHARE.equals(a21SubAccount.getSubAccountTypeCode());
536        }
537        else {
538            return false;
539        }
540
541    }
542    
543    /**
544     * Retrieves a prior year account from the persistence store
545     * @param chartOfAccountsCode the chart of accounts for the prior year account
546     * @param accountNumber the account number for the prior year account
547     * @return the PriorYearAccount
548     */
549    protected PriorYearAccount retrievePriorYearAccount(String chartOfAccountsCode, String accountNumber) {
550        Map pks = new HashMap();
551        pks.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, chartOfAccountsCode);
552        pks.put(OLEPropertyConstants.ACCOUNT_NUMBER, accountNumber);
553        
554        return (PriorYearAccount)this.getBusinessObjectService().findByPrimaryKey(PriorYearAccount.class, pks);
555    }
556    
557    /**
558     * 
559     * This method eases the institutional customization for Cost Sharing Object Codes for OriginEntries
560     * @param levelCode of the originEntry
561     * @param objectCode of the originEntry
562     * @return the new objectCode 
563     */
564    
565    protected String overrideCostShareObjectCode(String levelCode, String objectCode){
566        return objectCode;
567    }
568
569    /**
570     * Gets the parameterService attribute. 
571     * @return Returns the parameterService.
572     */
573    public ParameterService getParameterService() {
574        return parameterService;
575    }
576
577    /**
578     * Sets the parameterService attribute value.
579     * @param parameterService The parameterService to set.
580     */
581    public void setParameterService(ParameterService parameterService) {
582        this.parameterService = parameterService;
583    }
584
585    /**
586     * Gets the offsetDefinitionService attribute. 
587     * @return Returns the offsetDefinitionService.
588     */
589    public OffsetDefinitionService getOffsetDefinitionService() {
590        return offsetDefinitionService;
591    }
592
593    /**
594     * Sets the offsetDefinitionService attribute value.
595     * @param offsetDefinitionService The offsetDefinitionService to set.
596     */
597    public void setOffsetDefinitionService(OffsetDefinitionService offsetDefinitionService) {
598        this.offsetDefinitionService = offsetDefinitionService;
599    }
600
601    /**
602     * Gets the objectCodeService attribute. 
603     * @return Returns the objectCodeService.
604     */
605    public ObjectCodeService getObjectCodeService() {
606        return objectCodeService;
607    }
608
609    /**
610     * Sets the objectCodeService attribute value.
611     * @param objectCodeService The objectCodeService to set.
612     */
613    public void setObjectCodeService(ObjectCodeService objectCodeService) {
614        this.objectCodeService = objectCodeService;
615    }
616
617    /**
618     * Gets the dataDictionaryService attribute. 
619     * @return Returns the dataDictionaryService.
620     */
621    public DataDictionaryService getDataDictionaryService() {
622        return dataDictionaryService;
623    }
624
625    /**
626     * Sets the dataDictionaryService attribute value.
627     * @param dataDictionaryService The dataDictionaryService to set.
628     */
629    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
630        this.dataDictionaryService = dataDictionaryService;
631    }
632
633    /**
634     * Gets the flexibleOffsetAccountService attribute. 
635     * @return Returns the flexibleOffsetAccountService.
636     */
637    public FlexibleOffsetAccountService getFlexibleOffsetAccountService() {
638        return flexibleOffsetAccountService;
639    }
640
641    /**
642     * Sets the flexibleOffsetAccountService attribute value.
643     * @param flexibleOffsetAccountService The flexibleOffsetAccountService to set.
644     */
645    public void setFlexibleOffsetAccountService(FlexibleOffsetAccountService flexibleOffsetAccountService) {
646        this.flexibleOffsetAccountService = flexibleOffsetAccountService;
647    }
648
649    /**
650     * Gets the a21SubAccountService attribute. 
651     * @return Returns the a21SubAccountService.
652     */
653    public A21SubAccountService getA21SubAccountService() {
654        return a21SubAccountService;
655    }
656
657    /**
658     * Sets the a21SubAccountService attribute value.
659     * @param subAccountService The a21SubAccountService to set.
660     */
661    public void setA21SubAccountService(A21SubAccountService subAccountService) {
662        a21SubAccountService = subAccountService;
663    }
664
665    /**
666     * Gets the subObjectCodeService attribute. 
667     * @return Returns the subObjectCodeService.
668     */
669    public SubObjectCodeService getSubObjectCodeService() {
670        return subObjectCodeService;
671    }
672
673    /**
674     * Sets the subObjectCodeService attribute value.
675     * @param subObjectCodeService The subObjectCodeService to set.
676     */
677    public void setSubObjectCodeService(SubObjectCodeService subObjectCodeService) {
678        this.subObjectCodeService = subObjectCodeService;
679    }
680
681    /**
682     * Gets the optionsService attribute. 
683     * @return Returns the optionsService.
684     */
685    public OptionsService getOptionsService() {
686        return optionsService;
687    }
688
689    /**
690     * Sets the optionsService attribute value.
691     * @param optionsService The optionsService to set.
692     */
693    public void setOptionsService(OptionsService optionsService) {
694        this.optionsService = optionsService;
695    }
696
697    /**
698     * Gets the subFundGroupService attribute. 
699     * @return Returns the subFundGroupService.
700     */
701    public SubFundGroupService getSubFundGroupService() {
702        return subFundGroupService;
703    }
704
705    /**
706     * Sets the subFundGroupService attribute value.
707     * @param subFundGroupService The subFundGroupService to set.
708     */
709    public void setSubFundGroupService(SubFundGroupService subFundGroupService) {
710        this.subFundGroupService = subFundGroupService;
711    }
712
713    /**
714     * Gets the businessObjectService attribute. 
715     * @return Returns the businessObjectService.
716     */
717    public BusinessObjectService getBusinessObjectService() {
718        return businessObjectService;
719    }
720
721    /**
722     * Sets the businessObjectService attribute value.
723     * @param businessObjectService The businessObjectService to set.
724     */
725    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
726        this.businessObjectService = businessObjectService;
727    }
728
729    /**
730     * Gets the accountingCycleCachingService attribute. 
731     * @return Returns the accountingCycleCachingService.
732     */
733    public AccountingCycleCachingService getAccountingCycleCachingService() {
734        return accountingCycleCachingService;
735    }
736
737    /**
738     * Sets the accountingCycleCachingService attribute value.
739     * @param accountingCycleCachingService The accountingCycleCachingService to set.
740     */
741    public void setAccountingCycleCachingService(AccountingCycleCachingService accountingCycleCachingService) {
742        this.accountingCycleCachingService = accountingCycleCachingService;
743    }
744    
745}