/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.coa.service.impl; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.kuali.kfs.coa.businessobject.OrganizationReversion; import org.kuali.kfs.coa.businessobject.OrganizationReversionCategory; import org.kuali.kfs.coa.businessobject.OrganizationReversionDetail; import org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService; import org.kuali.kfs.sys.KFSKeyConstants; import org.kuali.rice.core.api.config.property.ConfigurationService; import org.kuali.rice.krad.bo.DocumentHeader; import org.kuali.rice.krad.bo.Note; import org.kuali.rice.krad.bo.PersistableBusinessObject; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.service.DocumentHeaderService; import org.kuali.rice.krad.service.NoteService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.ObjectUtils; /** * The default implementation of the OrganizationReversionDetailTrickleDownService */ public class OrganizationReversionDetailTrickleDownInactivationServiceImpl implements OrganizationReversionDetailTrickleDownInactivationService { private static final Logger LOG = Logger.getLogger(OrganizationReversionDetailTrickleDownInactivationServiceImpl.class); protected NoteService noteService; protected ConfigurationService kualiConfigurationService; protected BusinessObjectService businessObjectService; protected DocumentHeaderService documentHeaderService; /** * @see org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownInactiveOrganizationReversionDetails(org.kuali.kfs.coa.businessobject.OrganizationReversion, java.lang.String) */ public void trickleDownInactiveOrganizationReversionDetails(OrganizationReversion organizationReversion, String documentNumber) { organizationReversion.refreshReferenceObject("organizationReversionDetail"); trickleDownInactivations(organizationReversion.getOrganizationReversionDetail(), documentNumber); } /** * @see org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownInactiveOrganizationReversionDetails(org.kuali.kfs.coa.businessobject.OrganizationReversionCategory, java.lang.String) */ public void trickleDownInactiveOrganizationReversionDetails(OrganizationReversionCategory organizationReversionCategory, String documentNumber) { Map<String, Object> fieldValues = new HashMap<String, Object>(); fieldValues.put("organizationReversionCategoryCode", organizationReversionCategory.getOrganizationReversionCategoryCode()); Collection orgReversionDetails = businessObjectService.findMatching(OrganizationReversionDetail.class, fieldValues); List<OrganizationReversionDetail> organizationReversionDetailList = new ArrayList<OrganizationReversionDetail>(); for (Object orgRevDetailAsObject : orgReversionDetails) { organizationReversionDetailList.add((OrganizationReversionDetail)orgRevDetailAsObject); } trickleDownInactivations(organizationReversionDetailList, documentNumber); } /** * @see org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownActiveOrganizationReversionDetails(org.kuali.kfs.coa.businessobject.OrganizationReversion, java.lang.String) */ public void trickleDownActiveOrganizationReversionDetails(OrganizationReversion organizationReversion, String documentNumber) { organizationReversion.refreshReferenceObject("organizationReversionDetail"); trickleDownActivations(organizationReversion.getOrganizationReversionDetail(), documentNumber); } /** * @see org.kuali.kfs.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownActiveOrganizationReversionDetails(org.kuali.kfs.coa.businessobject.OrganizationReversionCategory, java.lang.String) */ public void trickleDownActiveOrganizationReversionDetails(OrganizationReversionCategory organizationReversionCategory, String documentNumber) { Map<String, Object> fieldValues = new HashMap<String, Object>(); fieldValues.put("organizationReversionCategoryCode", organizationReversionCategory.getOrganizationReversionCategoryCode()); Collection orgReversionDetails = businessObjectService.findMatching(OrganizationReversionDetail.class, fieldValues); List<OrganizationReversionDetail> organizationReversionDetailList = new ArrayList<OrganizationReversionDetail>(); for (Object orgRevDetailAsObject : orgReversionDetails) { organizationReversionDetailList.add((OrganizationReversionDetail)orgRevDetailAsObject); } trickleDownActivations(organizationReversionDetailList, documentNumber); } /** * The method which actually does the work of inactivating the details * @param organizationReversionDetails the details to inactivate * @param documentNumber the document number which has the inactivations as part of it * @return an inactivation status object which will help us save notes */ protected void trickleDownInactivations(List<OrganizationReversionDetail> organizationReversionDetails, String documentNumber) { TrickleDownStatus status = new TrickleDownStatus(KFSKeyConstants.ORGANIZATION_REVERSION_DETAIL_TRICKLE_DOWN_INACTIVATION, KFSKeyConstants.ORGANIZATION_REVERSION_DETAIL_TRICKLE_DOWN_INACTIVATION_ERROR_DURING_PERSISTENCE); if (!ObjectUtils.isNull(organizationReversionDetails) && !organizationReversionDetails.isEmpty()) { for (OrganizationReversionDetail detail : organizationReversionDetails) { if (detail.isActive()) { detail.setActive(false); try { businessObjectService.save(detail); status.addOrganizationReversionDetail(detail); } catch (RuntimeException re) { LOG.error("Unable to trickle-down inactivate sub-account " + detail.toString(), re); status.addErrorPersistingOrganizationReversionDetail(detail); } } } } status.saveSuccesfullyChangedNotes(documentNumber); status.saveErrorNotes(documentNumber); } /** * The method which actually does the work of activating the details * @param organizationReversionDetails the details to inactivate * @param documentNumber the document number which has the inactivations as part of it * @return an inactivation status object which will help us save notes */ protected void trickleDownActivations(List<OrganizationReversionDetail> organizationReversionDetails, String documentNumber) { TrickleDownStatus status = new TrickleDownStatus(KFSKeyConstants.ORGANIZATION_REVERSION_DETAIL_TRICKLE_DOWN_ACTIVATION, KFSKeyConstants.ORGANIZATION_REVERSION_DETAIL_TRICKLE_DOWN_ACTIVATION_ERROR_DURING_PERSISTENCE); if (!ObjectUtils.isNull(organizationReversionDetails) && !organizationReversionDetails.isEmpty()) { for (OrganizationReversionDetail detail : organizationReversionDetails) { if (!detail.isActive() && allowActivation(detail)) { detail.setActive(true); try { businessObjectService.save(detail); status.addOrganizationReversionDetail(detail); } catch (RuntimeException re) { LOG.error("Unable to trickle-down inactivate sub-account " + detail.toString(), re); status.addErrorPersistingOrganizationReversionDetail(detail); } } } } status.saveSuccesfullyChangedNotes(documentNumber); status.saveErrorNotes(documentNumber); } /** * Determines whether the given organization reversion detail can be activated: ie, that both its owning OrganizationReversion and its related * OrganizationReversionCategory are both active * @param detail the detail to check * @return true if the detail can be activated, false otherwise */ protected boolean allowActivation(OrganizationReversionDetail detail) { boolean result = true; if (!ObjectUtils.isNull(detail.getOrganizationReversion())) { result &= detail.getOrganizationReversion().isActive(); } if (!ObjectUtils.isNull(detail.getOrganizationReversionCategory())) { result &= detail.getOrganizationReversionCategory().isActive(); } return result; } /** * Inner class to keep track of what organization reversions were inactivated and which * had errors when the persisting of the inactivation was attempted */ protected class TrickleDownStatus { private List<OrganizationReversionDetail> organizationReversionDetails; private List<OrganizationReversionDetail> errorPersistingOrganizationReversionDetails; private String successfullyChangedOrganizationReversionDetailsMessageKey; private String erroredOutOrganizationReversionDetailsMessageKey; /** * Constructs a OrganizationReversionDetailTrickleDownInactivationServiceImpl */ public TrickleDownStatus(String successfullyChangedOrganizationReversionDetailsMessageKey, String erroredOutOrganizationReversionDetailsMessageKey) { organizationReversionDetails = new ArrayList<OrganizationReversionDetail>(); errorPersistingOrganizationReversionDetails = new ArrayList<OrganizationReversionDetail>(); this.successfullyChangedOrganizationReversionDetailsMessageKey = successfullyChangedOrganizationReversionDetailsMessageKey; this.erroredOutOrganizationReversionDetailsMessageKey = erroredOutOrganizationReversionDetailsMessageKey; } /** * Adds an organization reversion detail which had a successfully persisted activation to the message list * @param organizationReversionDetail the detail to add to the list */ public void addOrganizationReversionDetail(OrganizationReversionDetail organizationReversionDetail) { organizationReversionDetails.add(organizationReversionDetail); } /** * Adds an organization reversion detail which could not successful persist its activation to the error message list * @param organizationReversionDetail the detail to add to the list */ public void addErrorPersistingOrganizationReversionDetail(OrganizationReversionDetail organizationReversionDetail) { errorPersistingOrganizationReversionDetails.add(organizationReversionDetail); } /** * @return the number of details we want per note */ protected int getDetailsPerNote() { return 20; } /** * Builds a List of Notes out of a list of OrganizationReversionDescriptions * @param messageKey the key of the note text in ApplicationResources.properties * @param noteParent the thing to stick the note on * @param organizationReversionDetails the List of OrganizationReversionDetails to make notes about * @return a List of Notes */ protected List<Note> generateNotes(String messageKey, PersistableBusinessObject noteParent, List<OrganizationReversionDetail> organizationReversionDetails) { List<Note> notes = new ArrayList<Note>(); List<String> organizationReversionDetailsDescriptions = generateOrganizationReversionDetailsForNotes(organizationReversionDetails); Note noteTemplate = new Note(); for (String description : organizationReversionDetailsDescriptions) { if (!StringUtils.isBlank(description)) { notes.add(buildNote(description, messageKey, noteTemplate, noteParent)); } } return notes; } /** * Builds a note * @param description a description to put into the message of the note * @param messageKey the key of the note text in ApplicationResources.properties * @param noteTemplate the template for the note * @param noteParent the thing to stick the note on * @return the built note */ protected Note buildNote(String description, String messageKey, Note noteTemplate, PersistableBusinessObject noteParent) { Note note = null; try { final String noteTextTemplate = kualiConfigurationService.getPropertyValueAsString(messageKey); final String noteText = MessageFormat.format(noteTextTemplate, description); note = noteService.createNote(noteTemplate, noteParent, GlobalVariables.getUserSession().getPrincipalId()); note.setNoteText(noteText); } catch (Exception e) { // noteService.createNote throws *Exception*??? // weak!! throw new RuntimeException("Cannot create note", e); } return note; } /** * Builds organization reverion detail descriptions to populate notes * @param organizationReversionDetails the list of details to convert to notes * @return a List of notes */ protected List<String> generateOrganizationReversionDetailsForNotes(List<OrganizationReversionDetail> organizationReversionDetails) { List<String> orgRevDetailDescriptions = new ArrayList<String>(); if (organizationReversionDetails.size() > 0) { StringBuilder description = new StringBuilder(); description.append(getOrganizationReversionDetailDescription(organizationReversionDetails.get(0))); int count = 1; while (count < organizationReversionDetails.size()) { if (count % getDetailsPerNote() == 0) { // time for a new note orgRevDetailDescriptions.add(description.toString()); description = new StringBuilder(); } else { description.append(", "); } description.append(getOrganizationReversionDetailDescription(organizationReversionDetails.get(count))); count += 1; } // add the last description orgRevDetailDescriptions.add(description.toString()); } return orgRevDetailDescriptions; } /** * Beautifully and eloquently describes an organization reversion detail * @param organizationReversionDetail the organization reversion detail to describe * @return the funny, heart-breaking, and ultimately inspiring resultant description */ protected String getOrganizationReversionDetailDescription(OrganizationReversionDetail organizationReversionDetail) { return organizationReversionDetail.getChartOfAccountsCode() + " - " + organizationReversionDetail.getOrganizationCode() + " Category: " + organizationReversionDetail.getOrganizationReversionCategoryCode(); } /** * Saves notes to a document * @param organizationReversionDetails the details to make notes about * @param messageKey the message key of the text of the note * @param documentNumber the document number to write to */ protected void saveAllNotes(List<OrganizationReversionDetail> organizationReversionDetails, String messageKey, String documentNumber) { DocumentHeader noteParent = documentHeaderService.getDocumentHeaderById(documentNumber); List<Note> notes = generateNotes(messageKey, noteParent, organizationReversionDetails); noteService.saveNoteList(notes); } /** * Adds all the notes about successful inactivations * @param documentNumber document number to save them to */ public void saveSuccesfullyChangedNotes(String documentNumber) { saveAllNotes(organizationReversionDetails, successfullyChangedOrganizationReversionDetailsMessageKey, documentNumber); } /** * Adds all the notes about inactivations which couldn't be saved * @param documentNumber the document number to save them to */ public void saveErrorNotes(String documentNumber) { saveAllNotes(errorPersistingOrganizationReversionDetails, erroredOutOrganizationReversionDetailsMessageKey, documentNumber); } /** * Sets the erroredOutOrganizationReversionDetailsMessageKey attribute value. * @param erroredOutOrganizationReversionDetailsMessageKey The erroredOutOrganizationReversionDetailsMessageKey to set. */ public void setErroredOutOrganizationReversionDetailsMessageKey(String erroredOutOrganizationReversionDetailsMessageKey) { this.erroredOutOrganizationReversionDetailsMessageKey = erroredOutOrganizationReversionDetailsMessageKey; } /** * Sets the successfullyChangedOrganizationReversionDetailsMessageKey attribute value. * @param successfullyChangedOrganizationReversionDetailsMessageKey The successfullyChangedOrganizationReversionDetailsMessageKey to set. */ public void setSuccessfullyChangedOrganizationReversionDetailsMessageKey(String successfullyChangedOrganizationReversionDetailsMessageKey) { this.successfullyChangedOrganizationReversionDetailsMessageKey = successfullyChangedOrganizationReversionDetailsMessageKey; } } /** * Gets the kualiConfigurationService attribute. * @return Returns the kualiConfigurationService. */ public ConfigurationService getConfigurationService() { return kualiConfigurationService; } /** * Sets the kualiConfigurationService attribute value. * @param kualiConfigurationService The kualiConfigurationService to set. */ public void setConfigurationService(ConfigurationService kualiConfigurationService) { this.kualiConfigurationService = kualiConfigurationService; } /** * Gets the noteService attribute. * @return Returns the noteService. */ public NoteService getNoteService() { return noteService; } /** * Sets the noteService attribute value. * @param noteService The noteService to set. */ public void setNoteService(NoteService noteService) { this.noteService = noteService; } /** * Gets the businessObjectService attribute. * @return Returns the businessObjectService. */ public BusinessObjectService getBusinessObjectService() { return businessObjectService; } /** * Sets the businessObjectService attribute value. * @param businessObjectService The businessObjectService to set. */ public void setBusinessObjectService(BusinessObjectService businessObjectService) { this.businessObjectService = businessObjectService; } /** * Gets the documentHeaderService attribute. * @return Returns the documentHeaderService. */ public DocumentHeaderService getDocumentHeaderService() { return documentHeaderService; } /** * Sets the documentHeaderService attribute value. * @param documentHeaderService The documentHeaderService to set. */ public void setDocumentHeaderService(DocumentHeaderService documentHeaderService) { this.documentHeaderService = documentHeaderService; } }