View Javadoc
1   /*
2    * Copyright 2009 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.coa.service.impl;
17  
18  import java.text.MessageFormat;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.log4j.Logger;
27  import org.kuali.ole.coa.businessobject.OrganizationReversion;
28  import org.kuali.ole.coa.businessobject.OrganizationReversionCategory;
29  import org.kuali.ole.coa.businessobject.OrganizationReversionDetail;
30  import org.kuali.ole.coa.service.OrganizationReversionDetailTrickleDownInactivationService;
31  import org.kuali.ole.sys.OLEKeyConstants;
32  import org.kuali.rice.core.api.config.property.ConfigurationService;
33  import org.kuali.rice.krad.bo.DocumentHeader;
34  import org.kuali.rice.krad.bo.Note;
35  import org.kuali.rice.krad.bo.PersistableBusinessObject;
36  import org.kuali.rice.krad.service.BusinessObjectService;
37  import org.kuali.rice.krad.service.DocumentHeaderService;
38  import org.kuali.rice.krad.service.NoteService;
39  import org.kuali.rice.krad.util.GlobalVariables;
40  import org.kuali.rice.krad.util.ObjectUtils;
41  
42  /**
43   * The default implementation of the OrganizationReversionDetailTrickleDownService
44   */
45  public class OrganizationReversionDetailTrickleDownInactivationServiceImpl implements OrganizationReversionDetailTrickleDownInactivationService {
46      private static final Logger LOG = Logger.getLogger(OrganizationReversionDetailTrickleDownInactivationServiceImpl.class);
47      protected NoteService noteService;
48      protected ConfigurationService kualiConfigurationService;
49      protected BusinessObjectService businessObjectService;
50      protected DocumentHeaderService documentHeaderService;
51      
52      /**
53       * @see org.kuali.ole.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownInactiveOrganizationReversionDetails(org.kuali.ole.coa.businessobject.OrganizationReversion, java.lang.String)
54       */
55      public void trickleDownInactiveOrganizationReversionDetails(OrganizationReversion organizationReversion, String documentNumber) {
56          organizationReversion.refreshReferenceObject("organizationReversionDetail");
57          trickleDownInactivations(organizationReversion.getOrganizationReversionDetail(), documentNumber);
58      }
59  
60      /**
61       * @see org.kuali.ole.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownInactiveOrganizationReversionDetails(org.kuali.ole.coa.businessobject.OrganizationReversionCategory, java.lang.String)
62       */
63      public void trickleDownInactiveOrganizationReversionDetails(OrganizationReversionCategory organizationReversionCategory, String documentNumber) {
64          Map<String, Object> fieldValues = new HashMap<String, Object>();
65          fieldValues.put("organizationReversionCategoryCode", organizationReversionCategory.getOrganizationReversionCategoryCode());
66          Collection orgReversionDetails = businessObjectService.findMatching(OrganizationReversionDetail.class, fieldValues);
67          
68          List<OrganizationReversionDetail> organizationReversionDetailList = new ArrayList<OrganizationReversionDetail>();
69          for (Object orgRevDetailAsObject : orgReversionDetails) {
70              organizationReversionDetailList.add((OrganizationReversionDetail)orgRevDetailAsObject);
71          }
72          trickleDownInactivations(organizationReversionDetailList, documentNumber);
73      }
74      
75      /**
76       * @see org.kuali.ole.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownActiveOrganizationReversionDetails(org.kuali.ole.coa.businessobject.OrganizationReversion, java.lang.String)
77       */
78      public void trickleDownActiveOrganizationReversionDetails(OrganizationReversion organizationReversion, String documentNumber) {
79          organizationReversion.refreshReferenceObject("organizationReversionDetail");
80          trickleDownActivations(organizationReversion.getOrganizationReversionDetail(), documentNumber);
81      }
82  
83      /**
84       * @see org.kuali.ole.coa.service.OrganizationReversionDetailTrickleDownInactivationService#trickleDownActiveOrganizationReversionDetails(org.kuali.ole.coa.businessobject.OrganizationReversionCategory, java.lang.String)
85       */
86      public void trickleDownActiveOrganizationReversionDetails(OrganizationReversionCategory organizationReversionCategory, String documentNumber) {
87          Map<String, Object> fieldValues = new HashMap<String, Object>();
88          fieldValues.put("organizationReversionCategoryCode", organizationReversionCategory.getOrganizationReversionCategoryCode());
89          Collection orgReversionDetails = businessObjectService.findMatching(OrganizationReversionDetail.class, fieldValues);
90          
91          List<OrganizationReversionDetail> organizationReversionDetailList = new ArrayList<OrganizationReversionDetail>();
92          for (Object orgRevDetailAsObject : orgReversionDetails) {
93              organizationReversionDetailList.add((OrganizationReversionDetail)orgRevDetailAsObject);
94          }
95          trickleDownActivations(organizationReversionDetailList, documentNumber);
96      }
97  
98      /**
99       * 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 }