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.HashMap;
021 import java.util.Iterator;
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.Account;
028 import org.kuali.kfs.coa.businessobject.SubAccount;
029 import org.kuali.kfs.coa.service.SubAccountTrickleDownInactivationService;
030 import org.kuali.kfs.sys.KFSKeyConstants;
031 import org.kuali.kfs.sys.KFSPropertyConstants;
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.dao.MaintenanceDocumentDao;
036 import org.kuali.rice.kns.document.MaintenanceLock;
037 import org.kuali.rice.kns.maintenance.Maintainable;
038 import org.kuali.rice.kns.service.DocumentHeaderService;
039 import org.kuali.rice.kns.service.KualiConfigurationService;
040 import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
041 import org.kuali.rice.kns.service.NoteService;
042 import org.kuali.rice.kns.util.ObjectUtils;
043 import org.springframework.transaction.annotation.Transactional;
044
045 @Transactional
046 public class SubAccountTrickleDownInactivationServiceImpl implements SubAccountTrickleDownInactivationService {
047 private static final Logger LOG = Logger.getLogger(SubAccountTrickleDownInactivationServiceImpl.class);
048
049 protected MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
050 protected MaintenanceDocumentDao maintenanceDocumentDao;
051 protected NoteService noteService;
052 protected KualiConfigurationService kualiConfigurationService;
053 protected DocumentHeaderService documentHeaderService;
054
055 /**
056 * Will generate Maintenance Locks for all (active or not) sub-accounts in the system related to the inactivated account using the sub-account
057 * maintainable registered for the sub-account maintenance document
058 *
059 * This version of the method assumes that the sub-account maintainable only requires that the SubAccount BOClass, document number, and SubAccount
060 * instance only needs to be passed into it
061 * @see org.kuali.kfs.gl.service.SubAccountTrickleDownInactivationService#generateTrickleDownMaintenanceLocks(org.kuali.kfs.coa.businessobject.Account, java.lang.String)
062 */
063 public List<MaintenanceLock> generateTrickleDownMaintenanceLocks(Account inactivatedAccount, String documentNumber) {
064 inactivatedAccount.refreshReferenceObject(KFSPropertyConstants.SUB_ACCOUNTS);
065 List<MaintenanceLock> maintenanceLocks = new ArrayList<MaintenanceLock>();
066
067 Maintainable subAccountMaintainable;
068 try {
069 subAccountMaintainable = (Maintainable) maintenanceDocumentDictionaryService.getMaintainableClass(SubAccount.class.getName()).newInstance();
070 subAccountMaintainable.setBoClass(SubAccount.class);
071 subAccountMaintainable.setDocumentNumber(documentNumber);
072 }
073 catch (Exception e) {
074 LOG.error("Unable to instantiate SubAccount Maintainable" , e);
075 throw new RuntimeException("Unable to instantiate SubAccount Maintainable" , e);
076 }
077
078 if (ObjectUtils.isNotNull(inactivatedAccount.getSubAccounts()) && !inactivatedAccount.getSubAccounts().isEmpty()) {
079 for (Iterator<SubAccount> i = inactivatedAccount.getSubAccounts().iterator(); i.hasNext(); ) {
080 SubAccount subAccount = i.next();
081
082 subAccountMaintainable.setBusinessObject(subAccount);
083 maintenanceLocks.addAll(subAccountMaintainable.generateMaintenanceLocks());
084 }
085 }
086 return maintenanceLocks;
087 }
088
089 public void trickleDownInactivateSubAccounts(Account inactivatedAccount, String documentNumber) {
090 List<SubAccount> inactivatedSubAccounts = new ArrayList<SubAccount>();
091 Map<SubAccount, String> alreadyLockedSubAccounts = new HashMap<SubAccount, String>();
092 List<SubAccount> errorPersistingSubAccounts = new ArrayList<SubAccount>();
093
094 Maintainable subAccountMaintainable;
095 try {
096 subAccountMaintainable = (Maintainable) maintenanceDocumentDictionaryService.getMaintainableClass(SubAccount.class.getName()).newInstance();
097 subAccountMaintainable.setBoClass(SubAccount.class);
098 subAccountMaintainable.setDocumentNumber(documentNumber);
099 }
100 catch (Exception e) {
101 LOG.error("Unable to instantiate SubAccount Maintainable" , e);
102 throw new RuntimeException("Unable to instantiate SubAccount Maintainable" , e);
103 }
104
105 inactivatedAccount.refreshReferenceObject(KFSPropertyConstants.SUB_ACCOUNTS);
106 if (ObjectUtils.isNotNull(inactivatedAccount.getSubAccounts()) && !inactivatedAccount.getSubAccounts().isEmpty()) {
107 for (Iterator<SubAccount> i = inactivatedAccount.getSubAccounts().iterator(); i.hasNext(); ) {
108 SubAccount subAccount = i.next();
109 if (subAccount.isActive()) {
110 subAccountMaintainable.setBusinessObject(subAccount);
111 List<MaintenanceLock> subAccountLocks = subAccountMaintainable.generateMaintenanceLocks();
112
113 MaintenanceLock failedLock = verifyAllLocksFromThisDocument(subAccountLocks, documentNumber);
114 if (failedLock != null) {
115 // another document has locked this sub account, so we don't try to inactivate the account
116 alreadyLockedSubAccounts.put(subAccount, failedLock.getDocumentNumber());
117 }
118 else {
119 // no locks other than our own (but there may have been no locks at all), just go ahead and try to update
120 subAccount.setActive(false);
121
122 try {
123 subAccountMaintainable.saveBusinessObject();
124 inactivatedSubAccounts.add(subAccount);
125 }
126 catch (RuntimeException e) {
127 LOG.error("Unable to trickle-down inactivate sub-account " + subAccount.toString(), e);
128 errorPersistingSubAccounts.add(subAccount);
129 }
130 }
131 }
132 }
133
134 addNotesToDocument(documentNumber, inactivatedSubAccounts, alreadyLockedSubAccounts, errorPersistingSubAccounts);
135 }
136 }
137
138 protected void addNotesToDocument(String documentNumber, List<SubAccount> inactivatedSubAccounts, Map<SubAccount, String> alreadyLockedSubAccounts, List<SubAccount> errorPersistingSubAccounts) {
139 if (inactivatedSubAccounts.isEmpty() && alreadyLockedSubAccounts.isEmpty() && errorPersistingSubAccounts.isEmpty()) {
140 // if we didn't try to inactivate any sub-accounts, then don't bother
141 return;
142 }
143 DocumentHeader noteParent = documentHeaderService.getDocumentHeaderById(documentNumber);
144 Note newNote = new Note();
145
146 addNotes(documentNumber, inactivatedSubAccounts, KFSKeyConstants.SUB_ACCOUNT_TRICKLE_DOWN_INACTIVATION, noteParent, newNote);
147 addNotes(documentNumber, errorPersistingSubAccounts, KFSKeyConstants.SUB_ACCOUNT_TRICKLE_DOWN_INACTIVATION_ERROR_DURING_PERSISTENCE, noteParent, newNote);
148 addMaintenanceLockedNotes(documentNumber, alreadyLockedSubAccounts, KFSKeyConstants.SUB_ACCOUNT_TRICKLE_DOWN_INACTIVATION_RECORD_ALREADY_MAINTENANCE_LOCKED, noteParent, newNote);
149 }
150
151 protected void addMaintenanceLockedNotes(String documentNumber, Map<SubAccount, String> lockedSubAccounts, String messageKey, PersistableBusinessObject noteParent, Note noteTemplate) {
152 for (Map.Entry<SubAccount, String> entry : lockedSubAccounts.entrySet()) {
153 try {
154 SubAccount subAccount = entry.getKey();
155 String subAccountString = subAccount.getChartOfAccountsCode() + " - " + subAccount.getAccountNumber() + " - " + subAccount.getSubAccountNumber();
156 if (StringUtils.isNotBlank(subAccountString)) {
157 String noteTextTemplate = kualiConfigurationService.getPropertyString(messageKey);
158 String noteText = MessageFormat.format(noteTextTemplate, subAccountString, entry.getValue());
159 Note note = noteService.createNote(noteTemplate, noteParent);
160 note.setNoteText(noteText);
161 noteService.save(note);
162 }
163 }
164 catch (Exception e) {
165 LOG.error("Unable to create/save notes for document " + documentNumber, e);
166 throw new RuntimeException("Unable to create/save notes for document " + documentNumber, e);
167 }
168 }
169 }
170
171 protected void addNotes(String documentNumber, List<SubAccount> listOfSubAccounts, String messageKey, PersistableBusinessObject noteParent, Note noteTemplate) {
172 for (int i = 0; i < listOfSubAccounts.size(); i += getNumSubAccountsPerNote()) {
173 try {
174 String subAccountString = createSubAccountChunk(listOfSubAccounts, i, i + getNumSubAccountsPerNote());
175 if (StringUtils.isNotBlank(subAccountString)) {
176 String noteTextTemplate = kualiConfigurationService.getPropertyString(messageKey);
177 String noteText = MessageFormat.format(noteTextTemplate, subAccountString);
178 Note note = noteService.createNote(noteTemplate, noteParent);
179 note.setNoteText(noteText);
180 noteService.save(note);
181 }
182 }
183 catch (Exception e) {
184 LOG.error("Unable to create/save notes for document " + documentNumber, e);
185 throw new RuntimeException("Unable to create/save notes for document " + documentNumber, e);
186 }
187 }
188 }
189
190 protected String createSubAccountChunk(List<SubAccount> listOfSubAccounts, int startIndex, int endIndex) {
191 StringBuilder buf = new StringBuilder();
192 for (int i = startIndex; i < endIndex && i < listOfSubAccounts.size(); i++) {
193 SubAccount subAccount = listOfSubAccounts.get(i);
194 buf.append(subAccount.getChartOfAccountsCode()).append(" - ").append(subAccount.getAccountNumber()).append(" - ")
195 .append(subAccount.getSubAccountNumber());
196 if (i + 1 < endIndex && i + 1 < listOfSubAccounts.size()) {
197 buf.append(", ");
198 }
199 }
200 return buf.toString();
201 }
202
203 protected int getNumSubAccountsPerNote() {
204 return 20;
205 }
206
207 protected MaintenanceLock verifyAllLocksFromThisDocument(List<MaintenanceLock> maintenanceLocks, String documentNumber) {
208 for (MaintenanceLock maintenanceLock : maintenanceLocks) {
209 String lockingDocNumber = maintenanceDocumentDao.getLockingDocumentNumber(maintenanceLock.getLockingRepresentation(), documentNumber);
210 if (StringUtils.isNotBlank(lockingDocNumber)) {
211 return maintenanceLock;
212 }
213 }
214 return null;
215 }
216
217 public void setMaintenanceDocumentDictionaryService(MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService) {
218 this.maintenanceDocumentDictionaryService = maintenanceDocumentDictionaryService;
219 }
220
221
222
223 public void setMaintenanceDocumentDao(MaintenanceDocumentDao maintenanceDocumentDao) {
224 this.maintenanceDocumentDao = maintenanceDocumentDao;
225 }
226
227
228
229 public void setNoteService(NoteService noteService) {
230 this.noteService = noteService;
231 }
232
233 public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) {
234 this.kualiConfigurationService = kualiConfigurationService;
235 }
236
237 public void setDocumentHeaderService(DocumentHeaderService documentHeaderService) {
238 this.documentHeaderService = documentHeaderService;
239 }
240 }