View Javadoc
1   /*
2    * Copyright 2007 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.document;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.kuali.ole.coa.businessobject.ObjectCode;
24  import org.kuali.ole.coa.businessobject.ObjectCodeGlobal;
25  import org.kuali.ole.coa.businessobject.ObjectCodeGlobalDetail;
26  import org.kuali.ole.coa.service.ObjectCodeService;
27  import org.kuali.ole.coa.service.SubObjectTrickleDownInactivationService;
28  import org.kuali.ole.sys.OLEConstants;
29  import org.kuali.ole.sys.context.SpringContext;
30  import org.kuali.ole.sys.document.FinancialSystemGlobalMaintainable;
31  import org.kuali.rice.krad.bo.GlobalBusinessObject;
32  import org.kuali.rice.krad.bo.PersistableBusinessObject;
33  import org.kuali.rice.krad.maintenance.MaintenanceLock;
34  import org.kuali.rice.krad.service.BusinessObjectService;
35  import org.kuali.rice.krad.util.KRADConstants;
36  import org.kuali.rice.krad.util.ObjectUtils;
37  
38  /**
39   * This class provides some specific functionality for the {@link ObjectCodeGlobal} maintenance document refresh - sets the current
40   * fiscal year from the {@link ObjectCodeGlobalDetail} prepareGlobalsForSave - sets the object code on each detail object in the
41   * collection generateMaintenanceLocks - generates the appropriate maintenance locks for the {@link ObjectCode}
42   */
43  public class ObjectCodeGlobalMaintainableImpl extends FinancialSystemGlobalMaintainable {
44      private static String CHANGE_DETAIL_COLLECTION = "objectCodeGlobalDetails";
45  
46      //@Override
47      //public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) {
48      //  if our detail objects have a null fiscal year we need to fill these in with the "addLine" fiscal year
49      //      otherwise we leave it alone, these should only be null when coming back from a multiple value lookup
50      //    if (refreshCaller != null && refreshCaller.equals(OLEConstants.MULTIPLE_VALUE)) {
51      //        ObjectCodeGlobal objectCodeGlobal = (ObjectCodeGlobal) document.getDocumentBusinessObject();
52      //        ObjectCodeGlobalDetail addLineDetail = (ObjectCodeGlobalDetail) newCollectionLines.get(CHANGE_DETAIL_COLLECTION);
53      //        int fiscalYear = addLineDetail.getUniversityFiscalYear();
54      //        for (ObjectCodeGlobalDetail detail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
55      //            if (detail.getUniversityFiscalYear() == null) {
56      //                detail.setUniversityFiscalYear(fiscalYear);
57      //            }
58      //        }
59      //    }
60  
61      //    super.refresh(refreshCaller, fieldValues, document);
62      //}
63  
64      //
65      // @Override
66      // protected List<String> getMultiValueIdentifierList(Collection maintCollection) {
67      // List<String> identifierList = new ArrayList<String>();
68      // for (ObjectCodeGlobalDetail bo : (Collection<ObjectCodeGlobalDetail>)maintCollection) {
69      // identifierList.add(bo.getChartOfAccountsCode());
70      // }
71      // return identifierList;
72      // }
73  
74      // @Override
75      // protected boolean hasBusinessObjectExistedInLookupResult(BusinessObject bo, List<String> existingIdentifierList) {
76      // // default implementation does nothing
77      // if (existingIdentifierList.contains(((ObjectCodeGlobalDetail)bo).getChartOfAccountsCode())) {
78      // return true;
79      // }
80      // else {
81      // return false;
82      // }
83      // }
84  
85      /**
86       * This method sets the object code on each detail object in the collection
87       */
88      @Override
89      protected void prepareGlobalsForSave() {
90          // copy the object code down from the header into the details
91          ObjectCodeGlobal objectCodeGlobal = (ObjectCodeGlobal) getBusinessObject();
92  
93          for (ObjectCodeGlobalDetail detail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
94              detail.setFinancialObjectCode(objectCodeGlobal.getFinancialObjectCode());
95          }
96          super.prepareGlobalsForSave();
97      }
98  
99      /**
100      * This generates the appropriate maintenance locks for the {@link ObjectCode}
101      * 
102      * @see org.kuali.rice.kns.maintenance.Maintainable#generateMaintenanceLocks()
103      */
104     @Override
105     public List<MaintenanceLock> generateMaintenanceLocks() {
106         ObjectCodeGlobal objectCodeGlobal = (ObjectCodeGlobal) getBusinessObject();
107         List<MaintenanceLock> maintenanceLocks = new ArrayList();
108         SubObjectTrickleDownInactivationService subObjectTrickleDownInactivationService = SpringContext.getBean(SubObjectTrickleDownInactivationService.class);
109         
110         for (ObjectCodeGlobalDetail detail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
111             MaintenanceLock maintenanceLock = new MaintenanceLock();
112             StringBuffer lockrep = new StringBuffer();
113 
114             lockrep.append(ObjectCode.class.getName() + OLEConstants.Maintenance.AFTER_CLASS_DELIM);
115             lockrep.append("universityFiscalYear" + OLEConstants.Maintenance.AFTER_FIELDNAME_DELIM);
116             lockrep.append(detail.getUniversityFiscalYear() + OLEConstants.Maintenance.AFTER_VALUE_DELIM);
117             lockrep.append("chartOfAccountsCode" + OLEConstants.Maintenance.AFTER_FIELDNAME_DELIM);
118             lockrep.append(detail.getChartOfAccountsCode() + OLEConstants.Maintenance.AFTER_VALUE_DELIM);
119             lockrep.append("financialObjectCode" + OLEConstants.Maintenance.AFTER_FIELDNAME_DELIM);
120             lockrep.append(detail.getFinancialObjectCode());
121 
122             maintenanceLock.setDocumentNumber(objectCodeGlobal.getDocumentNumber());
123             maintenanceLock.setLockingRepresentation(lockrep.toString());
124             maintenanceLocks.add(maintenanceLock);
125             
126             ObjectCode objectCode = new ObjectCode();
127             objectCode.setUniversityFiscalYear(detail.getUniversityFiscalYear());
128             objectCode.setChartOfAccountsCode(detail.getChartOfAccountsCode());
129             objectCode.setFinancialObjectCode(detail.getFinancialObjectCode());
130             objectCode.setActive(objectCodeGlobal.isFinancialObjectActiveIndicator());
131             
132             if (isInactivatingObjectCode(objectCode)) {
133                 // if it turns out that the object code does not have associated sub-objects (either because the object code doesn't exist or doesn't have sub-objects)
134                 // then the generateTrickleDownMaintenanceLocks method returns an empty list 
135                 maintenanceLocks.addAll(subObjectTrickleDownInactivationService.generateTrickleDownMaintenanceLocks(objectCode, getDocumentNumber()));
136             }
137         }
138         return maintenanceLocks;
139     }
140     
141     /**
142      * @see org.kuali.rice.kns.maintenance.Maintainable#saveBusinessObject()
143      */
144     @Override
145     public void saveBusinessObject() {
146         BusinessObjectService boService = SpringContext.getBean(BusinessObjectService.class);
147         
148         GlobalBusinessObject gbo = (GlobalBusinessObject) businessObject;
149 
150         // delete any indicated BOs
151         List<PersistableBusinessObject> bosToDeactivate = gbo.generateDeactivationsToPersist();
152         if (bosToDeactivate != null) {
153             if (!bosToDeactivate.isEmpty()) {
154                 boService.save(bosToDeactivate);
155             }
156         }
157         
158         // OJB caches the any ObjectCodes that are retrieved from the database.  If multiple queries return the same row (identified by the PK
159         // values), OJB will return the same instance of the ObjectCode.  However, in generateGlobalChangesToPersist(), the ObjectCode returned by
160         // OJB is altered, meaning that any subsequent OJB calls will return the altered object.  The following cache will store the active statuses
161         // of object codes affected by this global document before generateGlobalChangesToPersist() alters them.
162         Map<String, Boolean> objectCodeActiveStatusCache = buildObjectCodeActiveStatusCache((ObjectCodeGlobal) gbo);
163         
164         SubObjectTrickleDownInactivationService subObjectTrickleDownInactivationService = SpringContext.getBean(SubObjectTrickleDownInactivationService.class);
165         // persist any indicated BOs
166         List<PersistableBusinessObject> bosToPersist = gbo.generateGlobalChangesToPersist();
167         if (bosToPersist != null) {
168             if (!bosToPersist.isEmpty()) {
169                 for (PersistableBusinessObject bo : bosToPersist) {
170                     ObjectCode objectCode = (ObjectCode) bo;
171                     
172                     boService.save(objectCode);
173                     
174                     if (isInactivatingObjectCode(objectCode, objectCodeActiveStatusCache)) {
175                         subObjectTrickleDownInactivationService.trickleDownInactivateSubObjects(objectCode, getDocumentNumber());
176                     }
177                 }
178             }
179         }
180     }
181     
182     protected boolean isInactivatingObjectCode(ObjectCode objectCode) {
183         ObjectCodeService objectCodeService = SpringContext.getBean(ObjectCodeService.class);
184         if (!objectCode.isActive()) {
185             ObjectCode objectCodeFromDB = objectCodeService.getByPrimaryId(objectCode.getUniversityFiscalYear(), objectCode.getChartOfAccountsCode(), objectCode.getFinancialObjectCode());
186             if (objectCodeFromDB != null && objectCodeFromDB.isActive()) {
187                 return true;
188             }
189         }
190         return false;
191     }
192     
193     protected boolean isInactivatingObjectCode(ObjectCode objectCode, Map<String, Boolean> objectCodeActiveStatusCache) {
194         if (!objectCode.isActive()) {
195             if (Boolean.TRUE.equals(objectCodeActiveStatusCache.get(buildObjectCodeCachingKey(objectCode)))) {
196                 return true;
197             }
198         }
199         return false;
200     }
201     
202     protected String buildObjectCodeCachingKey(ObjectCode objectCode) {
203         return objectCode.getUniversityFiscalYear() + KRADConstants.Maintenance.LOCK_AFTER_VALUE_DELIM + objectCode.getChartOfAccountsCode() + 
204                 KRADConstants.Maintenance.LOCK_AFTER_VALUE_DELIM + objectCode.getFinancialObjectCode(); 
205     }
206     
207     protected Map<String, Boolean> buildObjectCodeActiveStatusCache(ObjectCodeGlobal objectCodeGlobal) {
208         ObjectCodeService objectCodeService = SpringContext.getBean(ObjectCodeService.class);
209         Map<String, Boolean> cache = new HashMap<String, Boolean>();
210         for (ObjectCodeGlobalDetail detail : objectCodeGlobal.getObjectCodeGlobalDetails()) {
211             ObjectCode objectCodeFromDB = objectCodeService.getByPrimaryId(detail.getUniversityFiscalYear(), detail.getChartOfAccountsCode(), objectCodeGlobal.getFinancialObjectCode());
212             if (ObjectUtils.isNotNull(objectCodeFromDB)) {
213                 cache.put(buildObjectCodeCachingKey(objectCodeFromDB), Boolean.valueOf(objectCodeFromDB.isActive()));
214             }
215         }
216         return cache;
217     }
218 
219     @Override
220     public Class<? extends PersistableBusinessObject> getPrimaryEditedBusinessObjectClass() {
221         return ObjectCode.class;
222     }
223 }