View Javadoc
1   /*
2    * Copyright 2005 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.coa.service.impl;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.kuali.ole.coa.businessobject.Account;
27  import org.kuali.ole.coa.businessobject.Organization;
28  import org.kuali.ole.coa.service.ChartService;
29  import org.kuali.ole.coa.service.OrganizationService;
30  import org.kuali.ole.sys.OLEConstants.ChartApcParms;
31  import org.kuali.ole.sys.OLEPropertyConstants;
32  import org.kuali.ole.sys.businessobject.ChartOrgHolderImpl;
33  import org.kuali.ole.sys.service.NonTransactional;
34  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
35  import org.kuali.rice.krad.service.BusinessObjectService;
36  import org.springframework.cache.annotation.Cacheable;
37  
38  /**
39   * This class is the service implementation for the Org structure. This is the default implementation, that is delivered with Kuali.
40   */
41  
42  @NonTransactional
43  public class OrganizationServiceImpl implements OrganizationService {
44      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(OrganizationServiceImpl.class);
45      
46      protected ParameterService parameterService;
47      protected ChartService chartService;
48      protected BusinessObjectService boService;
49  
50      protected Map<ChartOrgHolderImpl,ChartOrgHolderImpl> parentOrgCache = null;
51  
52      /**
53       * 
54       * @see org.kuali.ole.coa.service.OrganizationService#getByPrimaryId(java.lang.String, java.lang.String)
55       */
56      @Override
57      public Organization getByPrimaryId(String chartOfAccountsCode, String organizationCode) {
58          Map<String, Object> keys = new HashMap<String, Object>();
59          keys.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, chartOfAccountsCode);
60          keys.put(OLEPropertyConstants.ORGANIZATION_CODE, organizationCode);
61          return boService.findByPrimaryKey(Organization.class, keys);
62      }
63  
64      /**
65       * Implements the getByPrimaryId method defined by OrganizationService. Method is used by KualiOrgReviewAttribute to enable
66       * caching of orgs for routing.
67       * 
68       * @see org.kuali.ole.coa.service.impl.OrganizationServiceImpl#getByPrimaryId(java.lang.String, java.lang.String)
69       */
70      @Override
71      @Cacheable(value=Organization.CACHE_NAME, key="#p0+'-'+#p1")
72      public Organization getByPrimaryIdWithCaching(String chartOfAccountsCode, String organizationCode) {
73          return getByPrimaryId(chartOfAccountsCode, organizationCode);
74      }
75  
76      /**
77       * @see org.kuali.ole.coa.service.OrganizationService#getActiveAccountsByOrg(java.lang.String, java.lang.String)
78       */
79      @Override
80      public List<Account> getActiveAccountsByOrg(String chartOfAccountsCode, String organizationCode) {
81  
82          if (StringUtils.isBlank(chartOfAccountsCode)) {
83              throw new IllegalArgumentException("String parameter chartOfAccountsCode was null or blank.");
84          }
85          if (StringUtils.isBlank(organizationCode)) {
86              throw new IllegalArgumentException("String parameter organizationCode was null or blank.");
87          }
88          
89          Map<String, Object> criteria = new HashMap<String, Object>();
90          criteria.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, chartOfAccountsCode);
91          criteria.put(OLEPropertyConstants.ORGANIZATION_CODE, organizationCode);
92          criteria.put(OLEPropertyConstants.ACTIVE, Boolean.TRUE);
93          return new ArrayList<Account>( boService.findMatching(Account.class, criteria) );
94      }
95  
96      /**
97       * @see org.kuali.ole.coa.service.OrganizationService#getActiveChildOrgs(java.lang.String, java.lang.String)
98       */
99      @Override
100     public List<Organization> getActiveChildOrgs(String chartOfAccountsCode, String organizationCode) {
101         if (StringUtils.isBlank(chartOfAccountsCode)) {
102             throw new IllegalArgumentException("String parameter chartOfAccountsCode was null or blank.");
103         }
104         if (StringUtils.isBlank(organizationCode)) {
105             throw new IllegalArgumentException("String parameter organizationCode was null or blank.");
106         }
107 
108         Map<String, Object> criteria = new HashMap<String, Object>();
109         criteria.put(OLEPropertyConstants.REPORTS_TO_CHART_OF_ACCOUNTS_CODE, chartOfAccountsCode);
110         criteria.put(OLEPropertyConstants.REPORTS_TO_ORGANIZATION_CODE, organizationCode);
111         criteria.put(OLEPropertyConstants.ACTIVE, Boolean.TRUE);
112 
113         return new ArrayList<Organization>( boService.findMatching(Organization.class, criteria) );
114     }
115 
116     protected void loadParentOrgCache() {
117         LOG.debug( "START - Initializing parent organization cache" );
118         Map<ChartOrgHolderImpl,ChartOrgHolderImpl> temp = new HashMap<ChartOrgHolderImpl, ChartOrgHolderImpl>();
119         
120         Collection<Organization> orgs = boService.findMatching(Organization.class, Collections.singletonMap(OLEPropertyConstants.ACTIVE, true));
121         for ( Organization org : orgs ) {
122             ChartOrgHolderImpl keyOrg = new ChartOrgHolderImpl(org);
123             if ( StringUtils.isNotBlank( org.getReportsToChartOfAccountsCode() ) 
124                     && StringUtils.isNotBlank( org.getReportsToOrganizationCode() ) ) {
125                 ChartOrgHolderImpl parentorg = new ChartOrgHolderImpl( org.getReportsToChartOfAccountsCode(), org.getReportsToOrganizationCode());
126                 temp.put(keyOrg, parentorg);
127             }
128         }
129         
130         parentOrgCache = temp;
131         if ( LOG.isDebugEnabled() ) {
132             LOG.debug( "COMPLETE - Initializing parent organization cache - " + temp.size() + " organizations loaded" );
133         }
134     }
135     
136     @Override
137     public void flushParentOrgCache() {
138         LOG.debug( "Flushing parent organization cache" );
139         parentOrgCache = null;
140     }
141 
142     @Override
143     public boolean isParentOrganization( String childChartOfAccountsCode, String childOrganizationCode, String parentChartOfAccountsCode, String parentOrganizationCode ) {
144         if (StringUtils.isBlank(childChartOfAccountsCode)
145                 || StringUtils.isBlank(childOrganizationCode)
146                 || StringUtils.isBlank(parentChartOfAccountsCode)
147                 || StringUtils.isBlank(parentOrganizationCode) ) {
148             return false;
149         }
150 
151         if ( parentOrgCache == null ) {
152             loadParentOrgCache();
153         }
154         
155         ChartOrgHolderImpl currOrg = new ChartOrgHolderImpl( childChartOfAccountsCode, childOrganizationCode );
156         ChartOrgHolderImpl desiredParentOrg = new ChartOrgHolderImpl( parentChartOfAccountsCode, parentOrganizationCode );
157         
158         // the the orgs are the same, we can short circuit the search right now
159         if ( currOrg.equals( desiredParentOrg ) ) {
160             return true;
161         }
162         
163         return isParentOrganization_Internal(currOrg, desiredParentOrg, new ArrayList<ChartOrgHolderImpl>() );
164     }
165 
166     /**
167      * This helper method handles the case where there might be cycles in the data.
168      * 
169      */
170     protected boolean isParentOrganization_Internal( ChartOrgHolderImpl currOrg, ChartOrgHolderImpl desiredParentOrg, List<ChartOrgHolderImpl> traversedOrgs ) {
171         
172         if ( traversedOrgs.contains(currOrg) ) {
173             LOG.error( "THERE IS A LOOP IN THE ORG DATA: " + currOrg + " found a second time after traversing the following orgs: " + traversedOrgs );
174             return false;
175         }
176         
177         ChartOrgHolderImpl parentOrg = parentOrgCache.get(currOrg);
178         
179         // we could not find it in the table, return false
180         if ( parentOrg == null ) {
181             return false;
182         }
183         // it is its own parent, then false (we reached the top and did not find a match)
184         if ( parentOrg.equals(currOrg) ) {
185             return false;
186         }
187         // check parent org against desired parent organization
188         if ( parentOrg.equals( desiredParentOrg ) ) {
189             return true;
190         }
191         // otherwise, we don't know yet - so re-call this method moving up to the next parent org 
192         traversedOrgs.add( currOrg );
193         return isParentOrganization_Internal(parentOrg, desiredParentOrg, traversedOrgs);
194     }
195         
196     /**
197      * 
198      * @see org.kuali.ole.coa.service.OrganizationService#getActiveOrgsByType(java.lang.String)
199      */
200     @Override
201     public List<Organization> getActiveOrgsByType(String organizationTypeCode) {
202         if (StringUtils.isBlank(organizationTypeCode)) {
203             throw new IllegalArgumentException("String parameter organizationTypeCode was null or blank.");
204         }
205         Map<String, Object> criteria = new HashMap<String, Object>();
206         criteria.put(OLEPropertyConstants.ORGANIZATION_TYPE_CODE, organizationTypeCode);
207         criteria.put(OLEPropertyConstants.ACTIVE, Boolean.TRUE);
208 
209         return new ArrayList<Organization>( boService.findMatching(Organization.class, criteria) );
210     }
211 
212     /**
213      * 
214      * @see org.kuali.ole.coa.service.OrganizationService#getActiveFinancialOrgs()
215      */
216     @Override
217     public List<Organization> getActiveFinancialOrgs() {
218         Map<String, Object> criteria = new HashMap<String, Object>();
219         criteria.put(OLEPropertyConstants.ORGANIZATION_IN_FINANCIAL_PROCESSING_INDICATOR, Boolean.TRUE);
220         criteria.put(OLEPropertyConstants.ACTIVE, Boolean.TRUE);
221         return new ArrayList<Organization>( boService.findMatching(Organization.class, criteria) );
222     }
223     
224     /**
225      * 
226      * TODO: refactor me to a ChartOrgHolder
227      * 
228      * @see org.kuali.ole.coa.service.OrganizationService#getRootOrganizationCode()
229      */
230     @Override
231     public String[] getRootOrganizationCode() {
232         String rootChart = chartService.getUniversityChart().getChartOfAccountsCode();
233         String selfReportsOrgType = parameterService.getParameterValueAsString(Organization.class, ChartApcParms.ORG_MUST_REPORT_TO_SELF_ORG_TYPES);
234         String[] returnValues = { null, null };
235         
236         Map<String, Object> criteria = new HashMap<String, Object>();
237         criteria.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, rootChart);
238         criteria.put(OLEPropertyConstants.ORGANIZATION_TYPE_CODE, selfReportsOrgType);
239         criteria.put(OLEPropertyConstants.ACTIVE, Boolean.TRUE);
240         
241         Collection<Organization> results = boService.findMatching(Organization.class, criteria);
242         if (results != null && !results.isEmpty()) {
243             Organization org = results.iterator().next();
244             returnValues[0] = org.getChartOfAccountsCode();
245             returnValues[1] = org.getOrganizationCode();       
246         }
247         
248         return returnValues;
249     }
250 
251     public void setParameterService(ParameterService parameterService) {
252         this.parameterService = parameterService;
253     }
254     public void setBusinessObjectService(BusinessObjectService boService) {
255         this.boService = boService;
256     }
257     public void setChartService(ChartService chartService) {
258         this.chartService = chartService;
259     }
260 
261 }