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