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