View Javadoc
1   /*
2    * Copyright 2008 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.HashMap;
21  import java.util.Iterator;
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.Account;
28  import org.kuali.ole.coa.businessobject.SubAccount;
29  import org.kuali.ole.coa.service.SubAccountTrickleDownInactivationService;
30  import org.kuali.ole.sys.OLEKeyConstants;
31  import org.kuali.ole.sys.OLEPropertyConstants;
32  import org.kuali.rice.core.api.config.property.ConfigurationService;
33  import org.kuali.rice.kns.maintenance.Maintainable;
34  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
35  import org.kuali.rice.krad.bo.DocumentHeader;
36  import org.kuali.rice.krad.bo.Note;
37  import org.kuali.rice.krad.bo.PersistableBusinessObject;
38  import org.kuali.rice.krad.dao.MaintenanceDocumentDao;
39  import org.kuali.rice.krad.maintenance.MaintenanceLock;
40  import org.kuali.rice.krad.service.DocumentHeaderService;
41  import org.kuali.rice.krad.service.NoteService;
42  import org.kuali.rice.krad.util.GlobalVariables;
43  import org.kuali.rice.krad.util.ObjectUtils;
44  import org.springframework.transaction.annotation.Transactional;
45  
46  @Transactional
47  public class SubAccountTrickleDownInactivationServiceImpl implements SubAccountTrickleDownInactivationService {
48      private static final Logger LOG = Logger.getLogger(SubAccountTrickleDownInactivationServiceImpl.class);
49  
50      protected MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
51      protected MaintenanceDocumentDao maintenanceDocumentDao;
52      protected NoteService noteService;
53      protected ConfigurationService kualiConfigurationService;
54      protected DocumentHeaderService documentHeaderService;
55      
56      /**
57       * Will generate Maintenance Locks for all (active or not) sub-accounts in the system related to the inactivated account using the sub-account
58       * maintainable registered for the sub-account maintenance document
59       * 
60       * This version of the method assumes that the sub-account maintainable only requires that the SubAccount BOClass, document number, and SubAccount
61       * instance only needs to be passed into it
62       * @see org.kuali.ole.gl.service.SubAccountTrickleDownInactivationService#generateTrickleDownMaintenanceLocks(org.kuali.ole.coa.businessobject.Account, java.lang.String)
63       */
64      public List<MaintenanceLock> generateTrickleDownMaintenanceLocks(Account inactivatedAccount, String documentNumber) {
65          inactivatedAccount.refreshReferenceObject(OLEPropertyConstants.SUB_ACCOUNTS);
66          List<MaintenanceLock> maintenanceLocks = new ArrayList<MaintenanceLock>();
67          
68          Maintainable subAccountMaintainable;
69          try {
70              subAccountMaintainable = (Maintainable) maintenanceDocumentDictionaryService.getMaintainableClass(SubAccount.class.getName()).newInstance();
71              subAccountMaintainable.setBoClass(SubAccount.class);
72              subAccountMaintainable.setDocumentNumber(documentNumber);
73          }
74          catch (Exception e) {
75              LOG.error("Unable to instantiate SubAccount Maintainable" , e);
76              throw new RuntimeException("Unable to instantiate SubAccount Maintainable" , e);
77          }
78          
79          if (ObjectUtils.isNotNull(inactivatedAccount.getSubAccounts()) && !inactivatedAccount.getSubAccounts().isEmpty()) {
80              for (Iterator<SubAccount> i = inactivatedAccount.getSubAccounts().iterator(); i.hasNext(); ) {
81                  SubAccount subAccount = i.next();
82                  
83                  subAccountMaintainable.setBusinessObject(subAccount);
84                  maintenanceLocks.addAll(subAccountMaintainable.generateMaintenanceLocks());
85              }
86          }
87          return maintenanceLocks;
88      }
89      
90      public void trickleDownInactivateSubAccounts(Account inactivatedAccount, String documentNumber) {
91          List<SubAccount> inactivatedSubAccounts = new ArrayList<SubAccount>();
92          Map<SubAccount, String> alreadyLockedSubAccounts = new HashMap<SubAccount, String>();
93          List<SubAccount> errorPersistingSubAccounts = new ArrayList<SubAccount>();
94          
95          Maintainable subAccountMaintainable;
96          try {
97              subAccountMaintainable = (Maintainable) maintenanceDocumentDictionaryService.getMaintainableClass(SubAccount.class.getName()).newInstance();
98              subAccountMaintainable.setBoClass(SubAccount.class);
99              subAccountMaintainable.setDocumentNumber(documentNumber);
100         }
101         catch (Exception e) {
102             LOG.error("Unable to instantiate SubAccount Maintainable" , e);
103             throw new RuntimeException("Unable to instantiate SubAccount Maintainable" , e);
104         }
105         
106         inactivatedAccount.refreshReferenceObject(OLEPropertyConstants.SUB_ACCOUNTS);
107         if (ObjectUtils.isNotNull(inactivatedAccount.getSubAccounts()) && !inactivatedAccount.getSubAccounts().isEmpty()) {
108             for (Iterator<SubAccount> i = inactivatedAccount.getSubAccounts().iterator(); i.hasNext(); ) {
109                 SubAccount subAccount = i.next();
110                 if (subAccount.isActive()) {
111                     subAccountMaintainable.setBusinessObject(subAccount);
112                     List<MaintenanceLock> subAccountLocks = subAccountMaintainable.generateMaintenanceLocks();
113                     
114                     MaintenanceLock failedLock = verifyAllLocksFromThisDocument(subAccountLocks, documentNumber);
115                     if (failedLock != null) {
116                         // another document has locked this sub account, so we don't try to inactivate the account
117                         alreadyLockedSubAccounts.put(subAccount, failedLock.getDocumentNumber());
118                     }
119                     else {
120                         // no locks other than our own (but there may have been no locks at all), just go ahead and try to update
121                         subAccount.setActive(false);
122                         
123                         try {
124                             subAccountMaintainable.saveBusinessObject();
125                             inactivatedSubAccounts.add(subAccount);
126                         }
127                         catch (RuntimeException e) {
128                             LOG.error("Unable to trickle-down inactivate sub-account " + subAccount.toString(), e);
129                             errorPersistingSubAccounts.add(subAccount);
130                         }
131                     }
132                 }
133             }
134             
135             addNotesToDocument(documentNumber, inactivatedSubAccounts, alreadyLockedSubAccounts, errorPersistingSubAccounts);
136         }
137     }
138 
139     protected void addNotesToDocument(String documentNumber, List<SubAccount> inactivatedSubAccounts, Map<SubAccount, String> alreadyLockedSubAccounts, List<SubAccount> errorPersistingSubAccounts) {
140         if (inactivatedSubAccounts.isEmpty() && alreadyLockedSubAccounts.isEmpty() && errorPersistingSubAccounts.isEmpty()) {
141             // if we didn't try to inactivate any sub-accounts, then don't bother
142             return;
143         }
144         DocumentHeader noteParent = documentHeaderService.getDocumentHeaderById(documentNumber);
145         Note newNote = new Note();
146         
147         addNotes(documentNumber, inactivatedSubAccounts, OLEKeyConstants.SUB_ACCOUNT_TRICKLE_DOWN_INACTIVATION, noteParent, newNote);
148         addNotes(documentNumber, errorPersistingSubAccounts, OLEKeyConstants.SUB_ACCOUNT_TRICKLE_DOWN_INACTIVATION_ERROR_DURING_PERSISTENCE, noteParent, newNote);
149         addMaintenanceLockedNotes(documentNumber, alreadyLockedSubAccounts, OLEKeyConstants.SUB_ACCOUNT_TRICKLE_DOWN_INACTIVATION_RECORD_ALREADY_MAINTENANCE_LOCKED, noteParent, newNote);
150     }
151     
152     protected void addMaintenanceLockedNotes(String documentNumber, Map<SubAccount, String> lockedSubAccounts, String messageKey, PersistableBusinessObject noteParent, Note noteTemplate) {
153         for (Map.Entry<SubAccount, String> entry : lockedSubAccounts.entrySet()) {
154             try {
155                 SubAccount subAccount = entry.getKey();
156                 String subAccountString = subAccount.getChartOfAccountsCode() + " - " + subAccount.getAccountNumber() + " - " + subAccount.getSubAccountNumber();
157                 if (StringUtils.isNotBlank(subAccountString)) {
158                     String noteTextTemplate = kualiConfigurationService.getPropertyValueAsString(messageKey);
159                     String noteText = MessageFormat.format(noteTextTemplate, subAccountString, entry.getValue());
160                     Note note = noteService.createNote(noteTemplate, noteParent, GlobalVariables.getUserSession().getPrincipalId());
161                     note.setNoteText(noteText);
162                     noteService.save(note);
163                 }
164             }
165             catch (Exception e) {
166                 LOG.error("Unable to create/save notes for document " + documentNumber, e);
167                 throw new RuntimeException("Unable to create/save notes for document " + documentNumber, e);
168             }
169         }
170     }
171 
172     protected void addNotes(String documentNumber, List<SubAccount> listOfSubAccounts, String messageKey, PersistableBusinessObject noteParent, Note noteTemplate) {
173         for (int i = 0; i < listOfSubAccounts.size(); i += getNumSubAccountsPerNote()) {
174             try {
175                 String subAccountString = createSubAccountChunk(listOfSubAccounts, i, i + getNumSubAccountsPerNote());
176                 if (StringUtils.isNotBlank(subAccountString)) {
177                     String noteTextTemplate = kualiConfigurationService.getPropertyValueAsString(messageKey);
178                     String noteText = MessageFormat.format(noteTextTemplate, subAccountString);
179                     Note note = noteService.createNote(noteTemplate, noteParent, GlobalVariables.getUserSession().getPrincipalId());
180                     note.setNoteText(noteText);
181                     note.setNotePostedTimestampToCurrent();
182                     noteService.save(note);
183                 }
184             }
185             catch (Exception e) {
186                 LOG.error("Unable to create/save notes for document " + documentNumber, e);
187                 throw new RuntimeException("Unable to create/save notes for document " + documentNumber, e);
188             }
189         }
190     }
191     
192     protected String createSubAccountChunk(List<SubAccount> listOfSubAccounts, int startIndex, int endIndex) {
193         StringBuilder buf = new StringBuilder(); 
194         for (int i = startIndex; i < endIndex && i < listOfSubAccounts.size(); i++) {
195             SubAccount subAccount = listOfSubAccounts.get(i);
196             buf.append(subAccount.getChartOfAccountsCode()).append(" - ").append(subAccount.getAccountNumber()).append(" - ")
197                     .append(subAccount.getSubAccountNumber());
198             if (i + 1 < endIndex && i + 1 < listOfSubAccounts.size()) {
199                 buf.append(", ");
200             }
201         }
202         return buf.toString();
203     }
204     
205     protected int getNumSubAccountsPerNote() {
206         return 20;
207     }
208     
209     protected MaintenanceLock verifyAllLocksFromThisDocument(List<MaintenanceLock> maintenanceLocks, String documentNumber) {
210         for (MaintenanceLock maintenanceLock : maintenanceLocks) {
211             String lockingDocNumber = maintenanceDocumentDao.getLockingDocumentNumber(maintenanceLock.getLockingRepresentation(), documentNumber);
212             if (StringUtils.isNotBlank(lockingDocNumber)) {
213                 return maintenanceLock;
214             }
215         }
216         return null;
217     }
218 
219     public void setMaintenanceDocumentDictionaryService(MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService) {
220         this.maintenanceDocumentDictionaryService = maintenanceDocumentDictionaryService;
221     }
222 
223 
224 
225     public void setMaintenanceDocumentDao(MaintenanceDocumentDao maintenanceDocumentDao) {
226         this.maintenanceDocumentDao = maintenanceDocumentDao;
227     }
228 
229 
230 
231     public void setNoteService(NoteService noteService) {
232         this.noteService = noteService;
233     }
234 
235     public void setConfigurationService(ConfigurationService kualiConfigurationService) {
236         this.kualiConfigurationService = kualiConfigurationService;
237     }
238 
239     public void setDocumentHeaderService(DocumentHeaderService documentHeaderService) {
240         this.documentHeaderService = documentHeaderService;
241     }
242 }