001/* 002 * Copyright 2005-2006 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.coa.service.impl; 017 018import java.sql.Date; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027 028import org.apache.commons.collections.IteratorUtils; 029import org.apache.commons.lang.StringUtils; 030import org.apache.log4j.Logger; 031import org.kuali.ole.coa.businessobject.Account; 032import org.kuali.ole.coa.businessobject.AccountDelegate; 033import org.kuali.ole.coa.dataaccess.AccountDao; 034import org.kuali.ole.coa.service.AccountService; 035import org.kuali.ole.sys.OLEConstants; 036import org.kuali.ole.sys.OLEConstants.SystemGroupParameterNames; 037import org.kuali.ole.sys.OLEPropertyConstants; 038import org.kuali.ole.sys.businessobject.AccountingLine; 039import org.kuali.ole.sys.context.SpringContext; 040import org.kuali.ole.sys.service.NonTransactional; 041import org.kuali.ole.sys.service.impl.OleParameterConstants; 042import org.kuali.rice.core.api.datetime.DateTimeService; 043import org.kuali.rice.coreservice.framework.parameter.ParameterService; 044import org.kuali.rice.kew.api.doctype.DocumentTypeService; 045import org.kuali.rice.kim.api.identity.Person; 046import org.kuali.rice.kim.util.KimCommonUtils; 047import org.kuali.rice.krad.service.BusinessObjectService; 048import org.kuali.rice.krad.util.ObjectUtils; 049import org.springframework.cache.annotation.Cacheable; 050 051/** 052 * This class is the service implementation for the Account structure. This is the default, Kuali provided implementation. 053 */ 054 055@NonTransactional 056public class AccountServiceImpl implements AccountService { 057 private static final Logger LOG = Logger.getLogger(AccountServiceImpl.class); 058 059 protected ParameterService parameterService; 060 protected AccountDao accountDao; 061 protected DateTimeService dateTimeService; 062 protected DocumentTypeService documentTypeService; 063 protected BusinessObjectService businessObjectService; 064 065 /** 066 * Retrieves an Account object based on primary key. 067 * 068 * @param chartOfAccountsCode - Chart of Accounts Code 069 * @param accountNumber - Account Number 070 * @return Account 071 * @see AccountService 072 */ 073 @Override 074 @Cacheable(value=Account.CACHE_NAME, key="#p0+'-'+#p1") 075 public Account getByPrimaryId(String chartOfAccountsCode, String accountNumber) { 076 if (LOG.isDebugEnabled()) { 077 LOG.debug("retrieving account by primaryId (" + chartOfAccountsCode + "," + accountNumber + ")"); 078 } 079 Map<String, Object> keys = new HashMap<String, Object>(2); 080 keys.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, chartOfAccountsCode); 081 keys.put(OLEPropertyConstants.ACCOUNT_NUMBER, accountNumber); 082 Account account = businessObjectService.findByPrimaryKey(Account.class, keys); 083 084 if (LOG.isDebugEnabled()) { 085 LOG.debug("retrieved account by primaryId (" + chartOfAccountsCode + "," + accountNumber + "): " + account ); 086 } 087 return account; 088 } 089 090 /** 091 * Method is used by KualiAccountAttribute to enable caching of accounts for routing. 092 * 093 * @see org.kuali.ole.coa.service.impl.AccountServiceImpl#getByPrimaryId(java.lang.String, java.lang.String) 094 */ 095 @Override 096 @Cacheable(value=Account.CACHE_NAME, key="#p0+'-'+#p1") 097 public Account getByPrimaryIdWithCaching(String chartOfAccountsCode, String accountNumber) { 098 Account account = getByPrimaryId(chartOfAccountsCode, accountNumber); 099 if ( account != null ) { 100 // force loading of chart reference object 101 account.getChartOfAccounts().getChartOfAccountsCode(); 102 } 103 return account; 104 } 105 106 /** 107 * @see org.kuali.ole.coa.service.AccountService#getAccountsThatUserIsResponsibleFor(org.kuali.bo.user.KualiUser) 108 */ 109 @Override 110 @Cacheable(value=Account.CACHE_NAME, key="'ResponsibleForAccounts'+#p0.principalId") 111 public List getAccountsThatUserIsResponsibleFor(Person person) { 112 if (LOG.isDebugEnabled()) { 113 LOG.debug("retrieving accountsResponsible list for user " + person.getName()); 114 } 115 116 // gets the list of accounts that the user is the Fiscal Officer of 117 List accountList = accountDao.getAccountsThatUserIsResponsibleFor(person, dateTimeService.getCurrentDate()); 118 if (LOG.isDebugEnabled()) { 119 LOG.debug("retrieved accountsResponsible list for user " + person.getName()); 120 } 121 return accountList; 122 } 123 124 /** 125 * @see org.kuali.ole.coa.service.AccountService#hasResponsibilityOnAccount(org.kuali.rice.kim.api.identity.Person, 126 * org.kuali.ole.coa.businessobject.Account) 127 */ 128 @Override 129 @Cacheable(value=Account.CACHE_NAME, key="'ResponsibilityOnAccount'+#p0.principalId+'-'+#p1.chartOfAccountsCode+'-'+#p1.accountNumber") 130 public boolean hasResponsibilityOnAccount(Person kualiUser, Account account) { 131 return accountDao.determineUserResponsibilityOnAccount(kualiUser, account, dateTimeService.getCurrentSqlDate()); 132 } 133 134 /** 135 * @see org.kuali.ole.coa.service.AccountService#getPrimaryDelegationByExample(org.kuali.ole.coa.businessobject.AccountDelegate, 136 * java.lang.String) 137 */ 138 139 @Override 140 public AccountDelegate getPrimaryDelegationByExample(AccountDelegate delegateExample, String totalDollarAmount) { 141 String documentTypeName = delegateExample.getFinancialDocumentTypeCode(); 142 Date currentSqlDate = dateTimeService.getCurrentSqlDate(); 143 List<AccountDelegate> primaryDelegations = filterAccountDelegates(delegateExample, accountDao.getPrimaryDelegationByExample(delegateExample, currentSqlDate, totalDollarAmount)); 144 if (primaryDelegations.isEmpty()) { 145 return null; 146 } 147 for (Iterator<AccountDelegate> iterator = primaryDelegations.iterator(); iterator.hasNext();) { 148 AccountDelegate delegate = iterator.next(); 149 if (!OLEConstants.ROOT_DOCUMENT_TYPE.equals(delegate.getFinancialDocumentTypeCode())) { 150 return delegate; 151 } 152 } 153 return primaryDelegations.iterator().next(); 154 } 155 156 /** 157 * @see org.kuali.ole.coa.service.AccountService#getSecondaryDelegationsByExample(org.kuali.ole.coa.businessobject.AccountDelegate, 158 * java.lang.String) 159 */ 160 @Override 161 public List getSecondaryDelegationsByExample(AccountDelegate delegateExample, String totalDollarAmount) { 162 Date currentSqlDate = dateTimeService.getCurrentSqlDate(); 163 List secondaryDelegations = accountDao.getSecondaryDelegationsByExample(delegateExample, currentSqlDate, totalDollarAmount); 164 return filterAccountDelegates(delegateExample, secondaryDelegations); 165 } 166 167 /** 168 * This method filters account delegates by 169 * 1) performing an exact match on the document type name of delegateExample 170 * 2) if no match is found for 1), then by performing an exact match on 171 * the closest parent document type name of delegateExample document type name. 172 * 173 * @param delegateExample 174 * @param accountDelegatesToFilterFrom 175 * @return 176 */ 177 protected List<AccountDelegate> filterAccountDelegates(AccountDelegate delegateExample, List<AccountDelegate> accountDelegatesToFilterFrom) { 178 String documentTypeName = delegateExample.getFinancialDocumentTypeCode(); 179 List<AccountDelegate> filteredAccountDelegates = filterAccountDelegates(accountDelegatesToFilterFrom, documentTypeName); 180 if (filteredAccountDelegates.size() == 0) { 181 Set<String> potentialParentDocumentTypeNames = getPotentialParentDocumentTypeNames(accountDelegatesToFilterFrom); 182 String closestParentDocumentTypeName = KimCommonUtils.getClosestParentDocumentTypeName(documentTypeService.getDocumentTypeByName(documentTypeName), potentialParentDocumentTypeNames); 183 filteredAccountDelegates = filterAccountDelegates(accountDelegatesToFilterFrom, closestParentDocumentTypeName); 184 } 185 return filteredAccountDelegates; 186 } 187 188 /** 189 * This method filters account delegates by performing an exact match on the document type name passed in. 190 * 191 * @param delegations 192 * @param documentTypeNameToFilterOn 193 * @return 194 */ 195 protected List<AccountDelegate> filterAccountDelegates(List<AccountDelegate> delegations, String documentTypeNameToFilterOn) { 196 List<AccountDelegate> filteredSecondaryDelegations = new ArrayList<AccountDelegate>(); 197 for (Object delegateObject : delegations) { 198 AccountDelegate delegate = (AccountDelegate) delegateObject; 199 if (StringUtils.equals(delegate.getFinancialDocumentTypeCode(), documentTypeNameToFilterOn)) { 200 filteredSecondaryDelegations.add(delegate); 201 } 202 } 203 return filteredSecondaryDelegations; 204 } 205 206 /** 207 * This method gets a list of potential parent document type names by collecting the unique doc type names from the list of 208 * account delegations 209 * 210 * @param delegations 211 * @return 212 */ 213 protected Set<String> getPotentialParentDocumentTypeNames(List<AccountDelegate> delegations) { 214 AccountDelegate delegate; 215 Set<String> potentialParentDocumentTypeNames = new HashSet<String>(); 216 for (Object delegateObject : delegations) { 217 delegate = (AccountDelegate) delegateObject; 218 if (!potentialParentDocumentTypeNames.contains(delegate.getFinancialDocumentTypeCode())) { 219 potentialParentDocumentTypeNames.add(delegate.getFinancialDocumentTypeCode()); 220 } 221 } 222 return potentialParentDocumentTypeNames; 223 } 224 225 /** 226 * get all accounts in the system. This is needed by a sufficient funds rebuilder job 227 * 228 * @return iterator of all accounts 229 */ 230 @Override 231 public Iterator getAllAccounts() { 232 LOG.debug("getAllAccounts() started"); 233 234 Iterator accountIter = accountDao.getAllAccounts(); 235 // FIXME: this loads all accounts into memory - could blow server 236 return IteratorUtils.toList(accountIter).iterator(); 237 } 238 239 /** 240 * @see org.kuali.ole.coa.service.AccountService#getActiveAccountsForAccountSupervisor(java.lang.String) 241 */ 242 @Override 243 public Iterator<Account> getActiveAccountsForAccountSupervisor(String principalId) { 244 return accountDao.getActiveAccountsForAccountSupervisor(principalId, dateTimeService.getCurrentSqlDate()); 245 } 246 247 /** 248 * @see org.kuali.ole.coa.service.AccountService#getActiveAccountsForFiscalOfficer(java.lang.String) 249 */ 250 @Override 251 public Iterator<Account> getActiveAccountsForFiscalOfficer(String principalId) { 252 return accountDao.getActiveAccountsForFiscalOfficer(principalId, dateTimeService.getCurrentSqlDate()); 253 } 254 255 /** 256 * @see org.kuali.ole.coa.service.AccountService#getExpiredAccountsForAccountSupervisor(java.lang.String) 257 */ 258 @Override 259 public Iterator<Account> getExpiredAccountsForAccountSupervisor(String principalId) { 260 return accountDao.getExpiredAccountsForAccountSupervisor(principalId, dateTimeService.getCurrentSqlDate()); 261 } 262 263 /** 264 * @see org.kuali.ole.coa.service.AccountService#getExpiredAccountsForFiscalOfficer(java.lang.String) 265 */ 266 @Override 267 public Iterator<Account> getExpiredAccountsForFiscalOfficer(String principalId) { 268 return accountDao.getExpiredAccountsForFiscalOfficer(principalId, dateTimeService.getCurrentSqlDate()); 269 } 270 271 /** 272 * @see org.kuali.ole.coa.service.AccountService#isPrincipalInAnyWayShapeOrFormAccountManager(java.lang.String) 273 */ 274 @Override 275 public boolean isPrincipalInAnyWayShapeOrFormAccountManager(String principalId) { 276 return accountDao.isPrincipalInAnyWayShapeOrFormAccountManager(principalId); 277 } 278 279 /** 280 * @see org.kuali.ole.coa.service.AccountService#isPrincipalInAnyWayShapeOrFormAccountSupervisor(java.lang.String) 281 */ 282 @Override 283 public boolean isPrincipalInAnyWayShapeOrFormAccountSupervisor(String principalId) { 284 return accountDao.isPrincipalInAnyWayShapeOrFormAccountSupervisor(principalId); 285 } 286 287 /** 288 * @see org.kuali.ole.coa.service.AccountService#isPrincipalInAnyWayShapeOrFormFiscalOfficer(java.lang.String) 289 */ 290 @Override 291 public boolean isPrincipalInAnyWayShapeOrFormFiscalOfficer(String principalId) { 292 return accountDao.isPrincipalInAnyWayShapeOrFormFiscalOfficer(principalId); 293 } 294 295 /** 296 * @see org.kuali.ole.coa.service.AccountService#getAccountsForAccountNumber(java.lang.String) 297 */ 298 @Override 299 @Cacheable(value=Account.CACHE_NAME, key="'AccountsForAccountNumber'+#p0") 300 public Collection<Account> getAccountsForAccountNumber(String accountNumber) { 301 return accountDao.getAccountsForAccountNumber(accountNumber); 302 } 303 304 305 @Override 306 public String getDefaultLaborBenefitRateCategoryCodeForAccountType(String accountTypeCode) { 307 String benefitRateCategory = parameterService.getSubParameterValueAsString(Account.class, "DEFAULT_BENEFIT_RATE_CATEGORY_CODE_BY_ACCOUNT_TYPE", accountTypeCode); 308 if ( StringUtils.isBlank(benefitRateCategory) ) { 309 benefitRateCategory = parameterService.getParameterValueAsString(Account.class, "DEFAULT_BENEFIT_RATE_CATEGORY_CODE"); 310 } 311 return StringUtils.trimToEmpty(benefitRateCategory); 312 } 313 314 /** 315 * @see org.kuali.ole.coa.service.AccountService#isFridgeBenefitCalculationEnable() 316 */ 317 @Override 318 public Boolean isFridgeBenefitCalculationEnable(){ 319 Boolean isFringeBeneCalcEnable = null; 320 321 //make sure the parameter exists 322 if(parameterService.parameterExists(OleParameterConstants.FINANCIAL_SYSTEM_ALL.class, "ENABLE_FRINGE_BENEFIT_CALC_BY_BENEFIT_RATE_CATEGORY_IND")){ 323 //check the system param to see if the labor benefit rate category should be editable 324 isFringeBeneCalcEnable = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(OleParameterConstants.FINANCIAL_SYSTEM_ALL.class, "ENABLE_FRINGE_BENEFIT_CALC_BY_BENEFIT_RATE_CATEGORY_IND"); 325 LOG.debug("System Parameter retrieved: " + isFringeBeneCalcEnable); 326 } 327 328 return (Boolean)org.apache.commons.lang.ObjectUtils.defaultIfNull(isFringeBeneCalcEnable, false); 329 } 330 331 332 /** 333 * @see org.kuali.ole.coa.service.AccountService#getUniqueAccountForAccountNumber(java.lang.String) 334 */ 335 @Override 336 @Cacheable(value=Account.CACHE_NAME, key="'UniqueAccountForAccountNumber'+#p0") 337 public Account getUniqueAccountForAccountNumber(String accountNumber) { 338 Iterator<Account> accounts = accountDao.getAccountsForAccountNumber(accountNumber).iterator(); 339 Account account = null; 340 // there should be only one account in the collection 341 if (accounts.hasNext()) { 342 account = accounts.next(); 343 } 344 return account; 345 } 346 347 /** 348 * @see org.kuali.ole.coa.service.AccountService#accountsCanCrossCharts() 349 */ 350 @Override 351 public boolean accountsCanCrossCharts() { 352 return parameterService.getParameterValueAsBoolean(OleParameterConstants.FINANCIAL_SYSTEM_ALL.class, SystemGroupParameterNames.ACCOUNTS_CAN_CROSS_CHARTS_IND); 353 } 354 355 /** 356 * @see org.kuali.ole.coa.service.AccountService#accountsCanCrossCharts() 357 */ 358 @Override 359 public void populateAccountingLineChartIfNeeded(AccountingLine line) { 360 if (!accountsCanCrossCharts() /* && line.getChartOfAccountsCode() == null */) { 361 Account account = getUniqueAccountForAccountNumber(line.getAccountNumber()); 362 if (ObjectUtils.isNotNull(account)) { 363 line.setChartOfAccountsCode(account.getChartOfAccountsCode()); 364 } 365 } 366 } 367 368 public void setAccountDao(AccountDao accountDao) { 369 this.accountDao = accountDao; 370 } 371 372 public void setDateTimeService(DateTimeService dateTimeService) { 373 this.dateTimeService = dateTimeService; 374 } 375 376 public void setDocumentTypeService(DocumentTypeService documentTypeService) { 377 this.documentTypeService = documentTypeService; 378 } 379 380 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 381 this.businessObjectService = businessObjectService; 382 } 383 384 public void setParameterService(ParameterService parameterService) { 385 this.parameterService = parameterService; 386 } 387}