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.coa.document; 017 018 import java.util.ArrayList; 019 import java.util.HashMap; 020 import java.util.List; 021 import java.util.Map; 022 023 import org.kuali.kfs.coa.businessobject.ObjectCode; 024 import org.kuali.kfs.coa.businessobject.ObjectCodeGlobal; 025 import org.kuali.kfs.coa.businessobject.ObjectCodeGlobalDetail; 026 import org.kuali.kfs.coa.service.ObjectCodeService; 027 import org.kuali.kfs.coa.service.SubObjectTrickleDownInactivationService; 028 import org.kuali.kfs.sys.KFSConstants; 029 import org.kuali.kfs.sys.context.SpringContext; 030 import org.kuali.kfs.sys.document.FinancialSystemGlobalMaintainable; 031 import org.kuali.rice.kns.bo.GlobalBusinessObject; 032 import org.kuali.rice.kns.bo.PersistableBusinessObject; 033 import org.kuali.rice.kns.document.MaintenanceDocument; 034 import org.kuali.rice.kns.document.MaintenanceLock; 035 import org.kuali.rice.kns.maintenance.KualiGlobalMaintainableImpl; 036 import org.kuali.rice.kns.service.BusinessObjectService; 037 import org.kuali.rice.kns.util.KNSConstants; 038 import org.kuali.rice.kns.util.ObjectUtils; 039 040 /** 041 * This class provides some specific functionality for the {@link ObjectCodeGlobal} maintenance document refresh - sets the current 042 * fiscal year from the {@link ObjectCodeGlobalDetail} prepareGlobalsForSave - sets the object code on each detail object in the 043 * collection generateMaintenanceLocks - generates the appropriate maintenance locks for the {@link ObjectCode} 044 */ 045 public class ObjectCodeGlobalMaintainableImpl extends FinancialSystemGlobalMaintainable { 046 private static String CHANGE_DETAIL_COLLECTION = "objectCodeGlobalDetails"; 047 048 //@Override 049 //public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) { 050 // if our detail objects have a null fiscal year we need to fill these in with the "addLine" fiscal year 051 // otherwise we leave it alone, these should only be null when coming back from a multiple value lookup 052 // if (refreshCaller != null && refreshCaller.equals(KFSConstants.MULTIPLE_VALUE)) { 053 // ObjectCodeGlobal objectCodeGlobal = (ObjectCodeGlobal) document.getDocumentBusinessObject(); 054 // ObjectCodeGlobalDetail addLineDetail = (ObjectCodeGlobalDetail) newCollectionLines.get(CHANGE_DETAIL_COLLECTION); 055 // int fiscalYear = addLineDetail.getUniversityFiscalYear(); 056 // for (ObjectCodeGlobalDetail detail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 057 // if (detail.getUniversityFiscalYear() == null) { 058 // detail.setUniversityFiscalYear(fiscalYear); 059 // } 060 // } 061 // } 062 063 // super.refresh(refreshCaller, fieldValues, document); 064 //} 065 066 // 067 // @Override 068 // protected List<String> getMultiValueIdentifierList(Collection maintCollection) { 069 // List<String> identifierList = new ArrayList<String>(); 070 // for (ObjectCodeGlobalDetail bo : (Collection<ObjectCodeGlobalDetail>)maintCollection) { 071 // identifierList.add(bo.getChartOfAccountsCode()); 072 // } 073 // return identifierList; 074 // } 075 076 // @Override 077 // protected boolean hasBusinessObjectExistedInLookupResult(BusinessObject bo, List<String> existingIdentifierList) { 078 // // default implementation does nothing 079 // if (existingIdentifierList.contains(((ObjectCodeGlobalDetail)bo).getChartOfAccountsCode())) { 080 // return true; 081 // } 082 // else { 083 // return false; 084 // } 085 // } 086 087 /** 088 * This method sets the object code on each detail object in the collection 089 */ 090 @Override 091 protected void prepareGlobalsForSave() { 092 // copy the object code down from the header into the details 093 ObjectCodeGlobal objectCodeGlobal = (ObjectCodeGlobal) getBusinessObject(); 094 095 for (ObjectCodeGlobalDetail detail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 096 detail.setFinancialObjectCode(objectCodeGlobal.getFinancialObjectCode()); 097 } 098 super.prepareGlobalsForSave(); 099 } 100 101 /** 102 * This generates the appropriate maintenance locks for the {@link ObjectCode} 103 * 104 * @see org.kuali.rice.kns.maintenance.Maintainable#generateMaintenanceLocks() 105 */ 106 @Override 107 public List<MaintenanceLock> generateMaintenanceLocks() { 108 ObjectCodeGlobal objectCodeGlobal = (ObjectCodeGlobal) getBusinessObject(); 109 List<MaintenanceLock> maintenanceLocks = new ArrayList(); 110 SubObjectTrickleDownInactivationService subObjectTrickleDownInactivationService = SpringContext.getBean(SubObjectTrickleDownInactivationService.class); 111 112 for (ObjectCodeGlobalDetail detail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 113 MaintenanceLock maintenanceLock = new MaintenanceLock(); 114 StringBuffer lockrep = new StringBuffer(); 115 116 lockrep.append(ObjectCode.class.getName() + KFSConstants.Maintenance.AFTER_CLASS_DELIM); 117 lockrep.append("universityFiscalYear" + KFSConstants.Maintenance.AFTER_FIELDNAME_DELIM); 118 lockrep.append(detail.getUniversityFiscalYear() + KFSConstants.Maintenance.AFTER_VALUE_DELIM); 119 lockrep.append("chartOfAccountsCode" + KFSConstants.Maintenance.AFTER_FIELDNAME_DELIM); 120 lockrep.append(detail.getChartOfAccountsCode() + KFSConstants.Maintenance.AFTER_VALUE_DELIM); 121 lockrep.append("financialObjectCode" + KFSConstants.Maintenance.AFTER_FIELDNAME_DELIM); 122 lockrep.append(detail.getFinancialObjectCode()); 123 124 maintenanceLock.setDocumentNumber(objectCodeGlobal.getDocumentNumber()); 125 maintenanceLock.setLockingRepresentation(lockrep.toString()); 126 maintenanceLocks.add(maintenanceLock); 127 128 ObjectCode objectCode = new ObjectCode(); 129 objectCode.setUniversityFiscalYear(detail.getUniversityFiscalYear()); 130 objectCode.setChartOfAccountsCode(detail.getChartOfAccountsCode()); 131 objectCode.setFinancialObjectCode(detail.getFinancialObjectCode()); 132 objectCode.setActive(objectCodeGlobal.isFinancialObjectActiveIndicator()); 133 134 if (isInactivatingObjectCode(objectCode)) { 135 // 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) 136 // then the generateTrickleDownMaintenanceLocks method returns an empty list 137 maintenanceLocks.addAll(subObjectTrickleDownInactivationService.generateTrickleDownMaintenanceLocks(objectCode, documentNumber)); 138 } 139 } 140 return maintenanceLocks; 141 } 142 143 /** 144 * @see org.kuali.rice.kns.maintenance.Maintainable#saveBusinessObject() 145 */ 146 @Override 147 public void saveBusinessObject() { 148 BusinessObjectService boService = SpringContext.getBean(BusinessObjectService.class); 149 150 GlobalBusinessObject gbo = (GlobalBusinessObject) businessObject; 151 152 // delete any indicated BOs 153 List<PersistableBusinessObject> bosToDeactivate = gbo.generateDeactivationsToPersist(); 154 if (bosToDeactivate != null) { 155 if (!bosToDeactivate.isEmpty()) { 156 boService.save(bosToDeactivate); 157 } 158 } 159 160 // OJB caches the any ObjectCodes that are retrieved from the database. If multiple queries return the same row (identified by the PK 161 // values), OJB will return the same instance of the ObjectCode. However, in generateGlobalChangesToPersist(), the ObjectCode returned by 162 // OJB is altered, meaning that any subsequent OJB calls will return the altered object. The following cache will store the active statuses 163 // of object codes affected by this global document before generateGlobalChangesToPersist() alters them. 164 Map<String, Boolean> objectCodeActiveStatusCache = buildObjectCodeActiveStatusCache((ObjectCodeGlobal) gbo); 165 166 SubObjectTrickleDownInactivationService subObjectTrickleDownInactivationService = SpringContext.getBean(SubObjectTrickleDownInactivationService.class); 167 // persist any indicated BOs 168 List<PersistableBusinessObject> bosToPersist = gbo.generateGlobalChangesToPersist(); 169 if (bosToPersist != null) { 170 if (!bosToPersist.isEmpty()) { 171 for (PersistableBusinessObject bo : bosToPersist) { 172 ObjectCode objectCode = (ObjectCode) bo; 173 174 boService.save(objectCode); 175 176 if (isInactivatingObjectCode(objectCode, objectCodeActiveStatusCache)) { 177 subObjectTrickleDownInactivationService.trickleDownInactivateSubObjects(objectCode, documentNumber); 178 } 179 } 180 } 181 } 182 } 183 184 protected boolean isInactivatingObjectCode(ObjectCode objectCode) { 185 ObjectCodeService objectCodeService = SpringContext.getBean(ObjectCodeService.class); 186 if (!objectCode.isActive()) { 187 ObjectCode objectCodeFromDB = objectCodeService.getByPrimaryId(objectCode.getUniversityFiscalYear(), objectCode.getChartOfAccountsCode(), objectCode.getFinancialObjectCode()); 188 if (objectCodeFromDB != null && objectCodeFromDB.isActive()) { 189 return true; 190 } 191 } 192 return false; 193 } 194 195 protected boolean isInactivatingObjectCode(ObjectCode objectCode, Map<String, Boolean> objectCodeActiveStatusCache) { 196 if (!objectCode.isActive()) { 197 if (Boolean.TRUE.equals(objectCodeActiveStatusCache.get(buildObjectCodeCachingKey(objectCode)))) { 198 return true; 199 } 200 } 201 return false; 202 } 203 204 protected String buildObjectCodeCachingKey(ObjectCode objectCode) { 205 return objectCode.getUniversityFiscalYear() + KNSConstants.Maintenance.AFTER_VALUE_DELIM + objectCode.getChartOfAccountsCode() + 206 KNSConstants.Maintenance.AFTER_VALUE_DELIM + objectCode.getFinancialObjectCode(); 207 } 208 209 protected Map<String, Boolean> buildObjectCodeActiveStatusCache(ObjectCodeGlobal objectCodeGlobal) { 210 ObjectCodeService objectCodeService = SpringContext.getBean(ObjectCodeService.class); 211 Map<String, Boolean> cache = new HashMap<String, Boolean>(); 212 for (ObjectCodeGlobalDetail detail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 213 ObjectCode objectCodeFromDB = objectCodeService.getByPrimaryId(detail.getUniversityFiscalYear(), detail.getChartOfAccountsCode(), objectCodeGlobal.getFinancialObjectCode()); 214 if (ObjectUtils.isNotNull(objectCodeFromDB)) { 215 cache.put(buildObjectCodeCachingKey(objectCodeFromDB), Boolean.valueOf(objectCodeFromDB.isActive())); 216 } 217 } 218 return cache; 219 } 220 221 @Override 222 public Class<? extends PersistableBusinessObject> getPrimaryEditedBusinessObjectClass() { 223 return ObjectCode.class; 224 } 225 }