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.service.impl; 017 018 import java.text.MessageFormat; 019 import java.util.ArrayList; 020 import java.util.Collection; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.apache.log4j.Logger; 027 import org.kuali.kfs.coa.businessobject.OrganizationReversion; 028 import org.kuali.kfs.coa.businessobject.OrganizationReversionCategory; 029 import org.kuali.kfs.coa.businessobject.OrganizationReversionDetail; 030 import org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService; 031 import org.kuali.kfs.sys.KFSKeyConstants; 032 import org.kuali.rice.kns.bo.DocumentHeader; 033 import org.kuali.rice.kns.bo.Note; 034 import org.kuali.rice.kns.bo.PersistableBusinessObject; 035 import org.kuali.rice.kns.service.BusinessObjectService; 036 import org.kuali.rice.kns.service.DocumentHeaderService; 037 import org.kuali.rice.kns.service.KualiConfigurationService; 038 import org.kuali.rice.kns.service.NoteService; 039 import org.kuali.rice.kns.util.ObjectUtils; 040 041 /** 042 * The default implementation of the OrganizationReversionDetailTrickleDownService 043 */ 044 public class OrganizationReversionDetailTrickleDownInactivationServiceImpl implements OrganizationReversionDetailTrickleDownInactivationService { 045 private static final Logger LOG = Logger.getLogger(OrganizationReversionDetailTrickleDownInactivationServiceImpl.class); 046 protected NoteService noteService; 047 protected KualiConfigurationService kualiConfigurationService; 048 protected BusinessObjectService businessObjectService; 049 protected DocumentHeaderService documentHeaderService; 050 051 /** 052 * @see org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownInactiveOrganizationReversionDetails(org.kuali.kfs.coa.businessobject.OrganizationReversion, java.lang.String) 053 */ 054 public void trickleDownInactiveOrganizationReversionDetails(OrganizationReversion organizationReversion, String documentNumber) { 055 organizationReversion.refreshReferenceObject("organizationReversionDetail"); 056 trickleDownInactivations(organizationReversion.getOrganizationReversionDetail(), documentNumber); 057 } 058 059 /** 060 * @see org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownInactiveOrganizationReversionDetails(org.kuali.kfs.coa.businessobject.OrganizationReversionCategory, java.lang.String) 061 */ 062 public void trickleDownInactiveOrganizationReversionDetails(OrganizationReversionCategory organizationReversionCategory, String documentNumber) { 063 Map<String, Object> fieldValues = new HashMap<String, Object>(); 064 fieldValues.put("organizationReversionCategoryCode", organizationReversionCategory.getOrganizationReversionCategoryCode()); 065 Collection orgReversionDetails = businessObjectService.findMatching(OrganizationReversionDetail.class, fieldValues); 066 067 List<OrganizationReversionDetail> organizationReversionDetailList = new ArrayList<OrganizationReversionDetail>(); 068 for (Object orgRevDetailAsObject : orgReversionDetails) { 069 organizationReversionDetailList.add((OrganizationReversionDetail)orgRevDetailAsObject); 070 } 071 trickleDownInactivations(organizationReversionDetailList, documentNumber); 072 } 073 074 /** 075 * @see org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownActiveOrganizationReversionDetails(org.kuali.kfs.coa.businessobject.OrganizationReversion, java.lang.String) 076 */ 077 public void trickleDownActiveOrganizationReversionDetails(OrganizationReversion organizationReversion, String documentNumber) { 078 organizationReversion.refreshReferenceObject("organizationReversionDetail"); 079 trickleDownActivations(organizationReversion.getOrganizationReversionDetail(), documentNumber); 080 } 081 082 /** 083 * @see org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownActiveOrganizationReversionDetails(org.kuali.kfs.coa.businessobject.OrganizationReversionCategory, java.lang.String) 084 */ 085 public void trickleDownActiveOrganizationReversionDetails(OrganizationReversionCategory organizationReversionCategory, String documentNumber) { 086 Map<String, Object> fieldValues = new HashMap<String, Object>(); 087 fieldValues.put("organizationReversionCategoryCode", organizationReversionCategory.getOrganizationReversionCategoryCode()); 088 Collection orgReversionDetails = businessObjectService.findMatching(OrganizationReversionDetail.class, fieldValues); 089 090 List<OrganizationReversionDetail> organizationReversionDetailList = new ArrayList<OrganizationReversionDetail>(); 091 for (Object orgRevDetailAsObject : orgReversionDetails) { 092 organizationReversionDetailList.add((OrganizationReversionDetail)orgRevDetailAsObject); 093 } 094 trickleDownActivations(organizationReversionDetailList, documentNumber); 095 } 096 097 /** 098 * The method which actually does the work of inactivating the details 099 * @param organizationReversionDetails the details to inactivate 100 * @param documentNumber the document number which has the inactivations as part of it 101 * @return an inactivation status object which will help us save notes 102 */ 103 protected void trickleDownInactivations(List<OrganizationReversionDetail> organizationReversionDetails, String documentNumber) { 104 TrickleDownStatus status = new TrickleDownStatus(KFSKeyConstants.ORGANIZATION_REVERSION_DETAIL_TRICKLE_DOWN_INACTIVATION, KFSKeyConstants.ORGANIZATION_REVERSION_DETAIL_TRICKLE_DOWN_INACTIVATION_ERROR_DURING_PERSISTENCE); 105 106 if (!ObjectUtils.isNull(organizationReversionDetails) && !organizationReversionDetails.isEmpty()) { 107 for (OrganizationReversionDetail detail : organizationReversionDetails) { 108 if (detail.isActive()) { 109 detail.setActive(false); 110 try { 111 businessObjectService.save(detail); 112 status.addOrganizationReversionDetail(detail); 113 } 114 catch (RuntimeException re) { 115 LOG.error("Unable to trickle-down inactivate sub-account " + detail.toString(), re); 116 status.addErrorPersistingOrganizationReversionDetail(detail); 117 } 118 } 119 } 120 } 121 122 status.saveSuccesfullyChangedNotes(documentNumber); 123 status.saveErrorNotes(documentNumber); 124 } 125 126 /** 127 * The method which actually does the work of activating the details 128 * @param organizationReversionDetails the details to inactivate 129 * @param documentNumber the document number which has the inactivations as part of it 130 * @return an inactivation status object which will help us save notes 131 */ 132 protected void trickleDownActivations(List<OrganizationReversionDetail> organizationReversionDetails, String documentNumber) { 133 TrickleDownStatus status = new TrickleDownStatus(KFSKeyConstants.ORGANIZATION_REVERSION_DETAIL_TRICKLE_DOWN_ACTIVATION, KFSKeyConstants.ORGANIZATION_REVERSION_DETAIL_TRICKLE_DOWN_ACTIVATION_ERROR_DURING_PERSISTENCE); 134 135 if (!ObjectUtils.isNull(organizationReversionDetails) && !organizationReversionDetails.isEmpty()) { 136 for (OrganizationReversionDetail detail : organizationReversionDetails) { 137 if (!detail.isActive() && allowActivation(detail)) { 138 detail.setActive(true); 139 try { 140 businessObjectService.save(detail); 141 status.addOrganizationReversionDetail(detail); 142 } 143 catch (RuntimeException re) { 144 LOG.error("Unable to trickle-down inactivate sub-account " + detail.toString(), re); 145 status.addErrorPersistingOrganizationReversionDetail(detail); 146 } 147 } 148 } 149 } 150 151 status.saveSuccesfullyChangedNotes(documentNumber); 152 status.saveErrorNotes(documentNumber); 153 } 154 155 /** 156 * Determines whether the given organization reversion detail can be activated: ie, that both its owning OrganizationReversion and its related 157 * OrganizationReversionCategory are both active 158 * @param detail the detail to check 159 * @return true if the detail can be activated, false otherwise 160 */ 161 protected boolean allowActivation(OrganizationReversionDetail detail) { 162 boolean result = true; 163 if (!ObjectUtils.isNull(detail.getOrganizationReversion())) { 164 result &= detail.getOrganizationReversion().isActive(); 165 } 166 if (!ObjectUtils.isNull(detail.getOrganizationReversionCategory())) { 167 result &= detail.getOrganizationReversionCategory().isActive(); 168 } 169 return result; 170 } 171 172 /** 173 * Inner class to keep track of what organization reversions were inactivated and which 174 * had errors when the persisting of the inactivation was attempted 175 */ 176 protected class TrickleDownStatus { 177 private List<OrganizationReversionDetail> organizationReversionDetails; 178 private List<OrganizationReversionDetail> errorPersistingOrganizationReversionDetails; 179 private String successfullyChangedOrganizationReversionDetailsMessageKey; 180 private String erroredOutOrganizationReversionDetailsMessageKey; 181 182 /** 183 * Constructs a OrganizationReversionDetailTrickleDownInactivationServiceImpl 184 */ 185 public TrickleDownStatus(String successfullyChangedOrganizationReversionDetailsMessageKey, String erroredOutOrganizationReversionDetailsMessageKey) { 186 organizationReversionDetails = new ArrayList<OrganizationReversionDetail>(); 187 errorPersistingOrganizationReversionDetails = new ArrayList<OrganizationReversionDetail>(); 188 this.successfullyChangedOrganizationReversionDetailsMessageKey = successfullyChangedOrganizationReversionDetailsMessageKey; 189 this.erroredOutOrganizationReversionDetailsMessageKey = erroredOutOrganizationReversionDetailsMessageKey; 190 } 191 192 /** 193 * Adds an organization reversion detail which had a successfully persisted activation to the message list 194 * @param organizationReversionDetail the detail to add to the list 195 */ 196 public void addOrganizationReversionDetail(OrganizationReversionDetail organizationReversionDetail) { 197 organizationReversionDetails.add(organizationReversionDetail); 198 } 199 200 /** 201 * Adds an organization reversion detail which could not successful persist its activation to the error message list 202 * @param organizationReversionDetail the detail to add to the list 203 */ 204 public void addErrorPersistingOrganizationReversionDetail(OrganizationReversionDetail organizationReversionDetail) { 205 errorPersistingOrganizationReversionDetails.add(organizationReversionDetail); 206 } 207 208 /** 209 * @return the number of details we want per note 210 */ 211 protected int getDetailsPerNote() { 212 return 20; 213 } 214 215 /** 216 * Builds a List of Notes out of a list of OrganizationReversionDescriptions 217 * @param messageKey the key of the note text in ApplicationResources.properties 218 * @param noteParent the thing to stick the note on 219 * @param organizationReversionDetails the List of OrganizationReversionDetails to make notes about 220 * @return a List of Notes 221 */ 222 protected List<Note> generateNotes(String messageKey, PersistableBusinessObject noteParent, List<OrganizationReversionDetail> organizationReversionDetails) { 223 List<Note> notes = new ArrayList<Note>(); 224 List<String> organizationReversionDetailsDescriptions = generateOrganizationReversionDetailsForNotes(organizationReversionDetails); 225 Note noteTemplate = new Note(); 226 for (String description : organizationReversionDetailsDescriptions) { 227 if (!StringUtils.isBlank(description)) { 228 notes.add(buildNote(description, messageKey, noteTemplate, noteParent)); 229 } 230 } 231 return notes; 232 } 233 234 /** 235 * Builds a note 236 * @param description a description to put into the message of the note 237 * @param messageKey the key of the note text in ApplicationResources.properties 238 * @param noteTemplate the template for the note 239 * @param noteParent the thing to stick the note on 240 * @return the built note 241 */ 242 protected Note buildNote(String description, String messageKey, Note noteTemplate, PersistableBusinessObject noteParent) { 243 Note note = null; 244 try { 245 final String noteTextTemplate = kualiConfigurationService.getPropertyString(messageKey); 246 final String noteText = MessageFormat.format(noteTextTemplate, description); 247 note = noteService.createNote(noteTemplate, noteParent); 248 note.setNoteText(noteText); 249 } 250 catch (Exception e) { 251 // noteService.createNote throws *Exception*??? 252 // weak!! 253 throw new RuntimeException("Cannot create note", e); 254 } 255 return note; 256 } 257 258 /** 259 * Builds organization reverion detail descriptions to populate notes 260 * @param organizationReversionDetails the list of details to convert to notes 261 * @return a List of notes 262 */ 263 protected List<String> generateOrganizationReversionDetailsForNotes(List<OrganizationReversionDetail> organizationReversionDetails) { 264 List<String> orgRevDetailDescriptions = new ArrayList<String>(); 265 266 if (organizationReversionDetails.size() > 0) { 267 StringBuilder description = new StringBuilder(); 268 description.append(getOrganizationReversionDetailDescription(organizationReversionDetails.get(0))); 269 270 int count = 1; 271 while (count < organizationReversionDetails.size()) { 272 if (count % getDetailsPerNote() == 0) { // time for a new note 273 orgRevDetailDescriptions.add(description.toString()); 274 description = new StringBuilder(); 275 } else { 276 description.append(", "); 277 } 278 description.append(getOrganizationReversionDetailDescription(organizationReversionDetails.get(count))); 279 count += 1; 280 } 281 282 // add the last description 283 orgRevDetailDescriptions.add(description.toString()); 284 } 285 286 return orgRevDetailDescriptions; 287 } 288 289 /** 290 * Beautifully and eloquently describes an organization reversion detail 291 * @param organizationReversionDetail the organization reversion detail to describe 292 * @return the funny, heart-breaking, and ultimately inspiring resultant description 293 */ 294 protected String getOrganizationReversionDetailDescription(OrganizationReversionDetail organizationReversionDetail) { 295 return organizationReversionDetail.getChartOfAccountsCode() + " - " + organizationReversionDetail.getOrganizationCode() + " Category: " + organizationReversionDetail.getOrganizationReversionCategoryCode(); 296 } 297 298 /** 299 * Saves notes to a document 300 * @param organizationReversionDetails the details to make notes about 301 * @param messageKey the message key of the text of the note 302 * @param documentNumber the document number to write to 303 */ 304 protected void saveAllNotes(List<OrganizationReversionDetail> organizationReversionDetails, String messageKey, String documentNumber) { 305 DocumentHeader noteParent = documentHeaderService.getDocumentHeaderById(documentNumber); 306 List<Note> notes = generateNotes(messageKey, noteParent, organizationReversionDetails); 307 noteService.saveNoteList(notes); 308 } 309 310 /** 311 * Adds all the notes about successful inactivations 312 * @param documentNumber document number to save them to 313 */ 314 public void saveSuccesfullyChangedNotes(String documentNumber) { 315 saveAllNotes(organizationReversionDetails, successfullyChangedOrganizationReversionDetailsMessageKey, documentNumber); 316 } 317 318 /** 319 * Adds all the notes about inactivations which couldn't be saved 320 * @param documentNumber the document number to save them to 321 */ 322 public void saveErrorNotes(String documentNumber) { 323 saveAllNotes(errorPersistingOrganizationReversionDetails, erroredOutOrganizationReversionDetailsMessageKey, documentNumber); 324 } 325 326 /** 327 * Sets the erroredOutOrganizationReversionDetailsMessageKey attribute value. 328 * @param erroredOutOrganizationReversionDetailsMessageKey The erroredOutOrganizationReversionDetailsMessageKey to set. 329 */ 330 public void setErroredOutOrganizationReversionDetailsMessageKey(String erroredOutOrganizationReversionDetailsMessageKey) { 331 this.erroredOutOrganizationReversionDetailsMessageKey = erroredOutOrganizationReversionDetailsMessageKey; 332 } 333 334 /** 335 * Sets the successfullyChangedOrganizationReversionDetailsMessageKey attribute value. 336 * @param successfullyChangedOrganizationReversionDetailsMessageKey The successfullyChangedOrganizationReversionDetailsMessageKey to set. 337 */ 338 public void setSuccessfullyChangedOrganizationReversionDetailsMessageKey(String successfullyChangedOrganizationReversionDetailsMessageKey) { 339 this.successfullyChangedOrganizationReversionDetailsMessageKey = successfullyChangedOrganizationReversionDetailsMessageKey; 340 } 341 } 342 343 /** 344 * Gets the kualiConfigurationService attribute. 345 * @return Returns the kualiConfigurationService. 346 */ 347 public KualiConfigurationService getKualiConfigurationService() { 348 return kualiConfigurationService; 349 } 350 351 /** 352 * Sets the kualiConfigurationService attribute value. 353 * @param kualiConfigurationService The kualiConfigurationService to set. 354 */ 355 public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) { 356 this.kualiConfigurationService = kualiConfigurationService; 357 } 358 359 /** 360 * Gets the noteService attribute. 361 * @return Returns the noteService. 362 */ 363 public NoteService getNoteService() { 364 return noteService; 365 } 366 367 /** 368 * Sets the noteService attribute value. 369 * @param noteService The noteService to set. 370 */ 371 public void setNoteService(NoteService noteService) { 372 this.noteService = noteService; 373 } 374 375 /** 376 * Gets the businessObjectService attribute. 377 * @return Returns the businessObjectService. 378 */ 379 public BusinessObjectService getBusinessObjectService() { 380 return businessObjectService; 381 } 382 383 /** 384 * Sets the businessObjectService attribute value. 385 * @param businessObjectService The businessObjectService to set. 386 */ 387 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 388 this.businessObjectService = businessObjectService; 389 } 390 391 /** 392 * Gets the documentHeaderService attribute. 393 * @return Returns the documentHeaderService. 394 */ 395 public DocumentHeaderService getDocumentHeaderService() { 396 return documentHeaderService; 397 } 398 399 /** 400 * Sets the documentHeaderService attribute value. 401 * @param documentHeaderService The documentHeaderService to set. 402 */ 403 public void setDocumentHeaderService(DocumentHeaderService documentHeaderService) { 404 this.documentHeaderService = documentHeaderService; 405 } 406 }