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.service.impl;
017    
018    import java.util.Calendar;
019    import java.util.HashMap;
020    
021    import org.kuali.kfs.coa.businessobject.Account;
022    import org.kuali.kfs.coa.businessobject.ObjectCode;
023    import org.kuali.kfs.coa.businessobject.OffsetDefinition;
024    import org.kuali.kfs.coa.service.AccountService;
025    import org.kuali.kfs.coa.service.ObjectCodeService;
026    import org.kuali.kfs.fp.businessobject.OffsetAccount;
027    import org.kuali.kfs.gl.businessobject.FlexibleAccountUpdateable;
028    import org.kuali.kfs.sys.KFSConstants;
029    import org.kuali.kfs.sys.exception.InvalidFlexibleOffsetException;
030    import org.kuali.kfs.sys.service.FlexibleOffsetAccountService;
031    import org.kuali.rice.kns.service.BusinessObjectService;
032    import org.kuali.rice.kns.service.DateTimeService;
033    import org.kuali.rice.kns.service.ParameterService;
034    
035    /**
036     * This is the default implementation of the FlexibleOffsetAccountService interface.
037     */
038    public class FlexibleOffsetAccountServiceImpl implements FlexibleOffsetAccountService {
039        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(FlexibleOffsetAccountServiceImpl.class);
040    
041        private BusinessObjectService businessObjectService;
042        private AccountService accountService;
043        private ObjectCodeService objectCodeService;
044        private DateTimeService dateTimeService;
045        private ParameterService parameterService;
046    
047        /**
048         * This method uses the parameters provided to retrieve an OffsetAccount instance if the flexible offset account flag is
049         * enabled.
050         * 
051         * @param chartOfAccountsCode The chart code used to retrieve the flexible offset account.
052         * @param accountNumber The account number of the flexible offset account being retrieved.
053         * @param financialOffsetObjectCode The offset object code used to retrieve the offset account.
054         * @return A flexible offset account based on the parameters provided, or null if offsets are not enabled.
055         * 
056         * @see FlexibleOffsetAccountService#getByPrimaryIdIfEnabled
057         */
058        public OffsetAccount getByPrimaryIdIfEnabled(String chartOfAccountsCode, String accountNumber, String financialOffsetObjectCode) {
059            LOG.debug("getByPrimaryIdIfEnabled() started");
060    
061            if (!getEnabled()) {
062                return null;
063            }
064            HashMap<String,Object> keys = new HashMap();
065            keys.put("chartOfAccountsCode", chartOfAccountsCode);
066            keys.put("accountNumber", accountNumber);
067            keys.put("financialOffsetObjectCode", financialOffsetObjectCode);
068            return (OffsetAccount) businessObjectService.findByPrimaryKey(OffsetAccount.class, keys);
069        }
070    
071        /**
072         * This method queries the parameter table to retrieve the value of the flexible offset flag and returns the resulting value.
073         * 
074         * @return True if flexible offsets are enabled, false otherwise. 
075         * 
076         * @see FlexibleOffsetAccountService#getEnabled
077         */
078        public boolean getEnabled() {
079            LOG.debug("getEnabled() started");
080            return parameterService.getIndicatorParameter(OffsetDefinition.class, KFSConstants.SystemGroupParameterNames.FLEXIBLE_OFFSET_ENABLED_FLAG);
081        }
082    
083        /**
084         * This method modifies the origin entry provided with values from the associated flexible offset account, which is 
085         * retrieved from the database using values provided by the origin entry.
086         * 
087         * @param originEntry The origin entry to be updated with offset account details.
088         * @return False if the flexible offset flag is false, if there is no corresponding flexbile offset account, true otherwise.
089         * 
090         * @see org.kuali.kfs.sys.service.FlexibleOffsetAccountService#updateOffset(org.kuali.kfs.gl.businessobject.OriginEntryFull)
091         */
092        public boolean updateOffset(FlexibleAccountUpdateable transaction) {
093            LOG.debug("setBusinessObjectService() started");
094    
095            if (!getEnabled()) {
096                return false;
097            }
098            String keyOfErrorMessage = "";
099    
100            Integer fiscalYear = transaction.getUniversityFiscalYear();
101            String chartOfAccountsCode = transaction.getChartOfAccountsCode();
102            String accountNumber = transaction.getAccountNumber();
103    
104            String balanceTypeCode = transaction.getFinancialBalanceTypeCode();
105            String documentTypeCode = transaction.getFinancialDocumentTypeCode();
106    
107            // do nothing if there is no the offset account with the given chart of accounts code,
108            // account number and offset object code in the offset table.
109            OffsetAccount flexibleOffsetAccount = getByPrimaryIdIfEnabled(chartOfAccountsCode, accountNumber, transaction.getFinancialObjectCode());
110            if (flexibleOffsetAccount == null) {
111                return false;
112            }
113    
114            String offsetAccountNumber = flexibleOffsetAccount.getFinancialOffsetAccountNumber();
115            String offsetChartOfAccountsCode = flexibleOffsetAccount.getFinancialOffsetChartOfAccountCode();
116    
117            Account offsetAccount = accountService.getByPrimaryId(offsetChartOfAccountsCode, offsetAccountNumber);
118            if (offsetAccount == null) {
119                throw new InvalidFlexibleOffsetException("Invalid Flexible Offset Account " + offsetChartOfAccountsCode + "-" + offsetAccountNumber);
120            }
121    
122            // Can't be closed and can't be expired
123            if (!offsetAccount.isActive()) {
124                throw new InvalidFlexibleOffsetException("Closed Flexible Offset Account " + offsetChartOfAccountsCode + "-" + offsetAccountNumber);
125            }
126            if ((offsetAccount.getAccountExpirationDate() != null) && isExpired(offsetAccount, dateTimeService.getCurrentCalendar())) {
127                throw new InvalidFlexibleOffsetException("Expired Flexible Offset Account " + offsetChartOfAccountsCode + "-" + offsetAccountNumber);
128            }
129    
130            // If the chart changes, make sure the object code is still valid
131            if (!chartOfAccountsCode.equals(offsetChartOfAccountsCode)) {
132                ObjectCode objectCode = objectCodeService.getByPrimaryId(fiscalYear, offsetChartOfAccountsCode, transaction.getFinancialObjectCode());
133                if (objectCode == null) {
134                    throw new InvalidFlexibleOffsetException("Invalid Object Code for flexible offset " + fiscalYear + "-" + offsetChartOfAccountsCode + "-" + transaction.getFinancialObjectCode());
135                }
136            }
137    
138            // replace the chart and account of the given transaction with those of the offset account obtained above
139            transaction.setAccount(offsetAccount);
140            transaction.setAccountNumber(offsetAccountNumber);
141            transaction.setChartOfAccountsCode(offsetChartOfAccountsCode);
142    
143            // blank out the sub account and sub object since the account has been replaced
144            transaction.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
145            transaction.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
146            return true;
147        }
148    
149        /**
150         * This method determines if an account has expired.  An account has expired if the expiration year of the account is 
151         * less than the run date year or if the date of expiration occurred before the run date provided.
152         * 
153         * @param account The account to be examined.
154         * @param runCalendar The date the expiration date is tested against.
155         * @return True if the account has expired, false otherwise.
156         */
157        protected boolean isExpired(Account account, Calendar runCalendar) {
158    
159            Calendar expirationDate = Calendar.getInstance();
160            expirationDate.setTimeInMillis(account.getAccountExpirationDate().getTime());
161    
162            int expirationYear = expirationDate.get(Calendar.YEAR);
163            int runYear = runCalendar.get(Calendar.YEAR);
164            int expirationDay = expirationDate.get(Calendar.DAY_OF_YEAR);
165            int runDay = runCalendar.get(Calendar.DAY_OF_YEAR);
166    
167            return (expirationYear < runYear) || (expirationYear == runYear && expirationDay < runDay);
168        }
169    
170        /**
171         * Sets the local dateTimeService attribute.
172         * @param dateTimeService The DateTimeService instance to be set.
173         */
174        public void setDateTimeService(DateTimeService dateTimeService) {
175            this.dateTimeService = dateTimeService;
176        }
177    
178        /**
179         * Sets the local accountService attribute.
180         * @param accountService The AccountService instance to be set.
181         */
182        public void setAccountService(AccountService accountService) {
183            this.accountService = accountService;
184        }
185    
186        /**
187         * Sets the local objectCodeService attribute.
188         * @param objectCodeService The ObjectCodeService instance to be set.
189         */
190        public void setObjectCodeService(ObjectCodeService objectCodeService) {
191            this.objectCodeService = objectCodeService;
192        }
193    
194        /**
195         * Sets the local businessObjectService attribute.
196         * @param businessObjectService The BusinessObjectService instance to be set.
197         */
198        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
199            this.businessObjectService = businessObjectService;
200        }
201    
202        /**
203         * Sets the local parameterService attribute.
204         * @param parameterService The ParameterService instance to be set.
205         */
206        public void setParameterService(ParameterService parameterService) {
207            this.parameterService = parameterService;
208        }
209    }