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