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.document; 017 018 import java.sql.Date; 019 import java.util.Iterator; 020 import java.util.List; 021 import java.util.Map; 022 023 import org.apache.commons.lang.StringUtils; 024 import org.apache.log4j.Logger; 025 import org.kuali.kfs.coa.businessobject.Account; 026 import org.kuali.kfs.coa.service.AccountPersistenceStructureService; 027 import org.kuali.kfs.coa.service.AccountService; 028 import org.kuali.kfs.coa.service.SubAccountTrickleDownInactivationService; 029 import org.kuali.kfs.coa.service.SubObjectTrickleDownInactivationService; 030 import org.kuali.kfs.sys.KFSPropertyConstants; 031 import org.kuali.kfs.sys.context.SpringContext; 032 import org.kuali.kfs.sys.document.FinancialSystemMaintainable; 033 import org.kuali.rice.kns.bo.PersistableBusinessObject; 034 import org.kuali.rice.kns.document.MaintenanceDocument; 035 import org.kuali.rice.kns.document.MaintenanceLock; 036 import org.kuali.rice.kns.service.DateTimeService; 037 import org.kuali.rice.kns.util.KNSConstants; 038 import org.kuali.rice.kns.util.ObjectUtils; 039 040 /** 041 * This class overrides the saveBusinessObject() method which is called during post process from the KualiPostProcessor so that it 042 * can automatically deactivate the Sub-Accounts related to the account It also overrides the processAfterCopy so that it sets 043 * specific fields that shouldn't be copied to default values {@link KualiPostProcessor} 044 */ 045 public class KualiAccountMaintainableImpl extends FinancialSystemMaintainable { 046 private static final Logger LOG = Logger.getLogger(KualiAccountMaintainableImpl.class); 047 private static final String ACCOUNT_GUIDE_LINE_PROPERTY = "accountGuideline"; 048 049 /** 050 * Automatically deactivates {@link SubAccount}s after saving the {@link Account} 051 * 052 * @see org.kuali.rice.kns.maintenance.Maintainable#saveBusinessObject() 053 */ 054 @Override 055 public void saveBusinessObject() { 056 boolean isClosingAccount = isClosingAccount(); 057 058 // make sure we save account first 059 super.saveBusinessObject(); 060 061 // if we're closing the account, then rely on the trickle-down inactivation services to trickle-down inactivate the 062 // sub-accounts 063 if (isClosingAccount) { 064 SpringContext.getBean(SubAccountTrickleDownInactivationService.class).trickleDownInactivateSubAccounts((Account) getBusinessObject(), documentNumber); 065 SpringContext.getBean(SubObjectTrickleDownInactivationService.class).trickleDownInactivateSubObjects((Account) getBusinessObject(), documentNumber); 066 } 067 } 068 069 /** 070 * After a copy is done set specific fields on {@link Account} to default values 071 * 072 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterCopy() 073 */ 074 @Override 075 public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) { 076 Account account = (Account) this.getBusinessObject(); 077 account.setAccountCreateDate(null); // account's pre-rules will fill this field in 078 account.setAccountEffectiveDate(new Date(SpringContext.getBean(DateTimeService.class).getCurrentDate().getTime())); 079 account.setActive(true); 080 super.processAfterCopy(document, parameters); 081 } 082 083 084 @Override 085 public List<MaintenanceLock> generateMaintenanceLocks() { 086 List<MaintenanceLock> maintenanceLocks = super.generateMaintenanceLocks(); 087 boolean isClosingAccount = false; 088 089 if (isClosingAccount()) { 090 maintenanceLocks.addAll(SpringContext.getBean(SubAccountTrickleDownInactivationService.class).generateTrickleDownMaintenanceLocks((Account) getBusinessObject(), documentNumber)); 091 maintenanceLocks.addAll(SpringContext.getBean(SubObjectTrickleDownInactivationService.class).generateTrickleDownMaintenanceLocks((Account) getBusinessObject(), documentNumber)); 092 } 093 return maintenanceLocks; 094 } 095 096 protected Account retrieveExistingAccountFromDB() { 097 Account newAccount = (Account) getBusinessObject(); 098 Account oldAccount = SpringContext.getBean(AccountService.class).getByPrimaryId(newAccount.getChartOfAccountsCode(), newAccount.getAccountNumber()); 099 return oldAccount; 100 } 101 102 protected boolean isClosingAccount() { 103 // the account has to be closed on the new side when editing in order for it to be possible that we are closing the account 104 if (KNSConstants.MAINTENANCE_EDIT_ACTION.equals(getMaintenanceAction()) && !((Account) getBusinessObject()).isActive()) { 105 Account existingAccountFromDB = retrieveExistingAccountFromDB(); 106 if (ObjectUtils.isNotNull(existingAccountFromDB)) { 107 // now see if the original account was not closed, in which case, we are closing the account 108 if (existingAccountFromDB.isActive()) { 109 return true; 110 } 111 } 112 } 113 return false; 114 } 115 116 /** 117 * Determines who should be FYI'd as the account supervisor for the routing of the account maintenance document. If there is an 118 * existing account, it uses the account supervisor from that; otherwise, it uses the account supervisor from the business 119 * object of this maintainable 120 * 121 * @return an appropriate account supervisor to FYI during account maintenance document routing 122 */ 123 public String getRoutingAccountsSupervisorySystemsIdentifier() { 124 final Account existingAccountFromDB = retrieveExistingAccountFromDB(); 125 if (ObjectUtils.isNull(existingAccountFromDB)) { 126 return ((Account) getBusinessObject()).getAccountsSupervisorySystemsIdentifier(); 127 } 128 return existingAccountFromDB.getAccountsSupervisorySystemsIdentifier(); 129 } 130 131 /** 132 * Had to override this method because account guideline data was lost when copied and then a lookup is performed 133 * 134 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#refresh(java.lang.String, java.util.Map, 135 * org.kuali.rice.kns.document.MaintenanceDocument) 136 */ 137 @Override 138 public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) { 139 super.refresh(refreshCaller, fieldValues, document); 140 Account newAcct = (Account) document.getNewMaintainableObject().getBusinessObject(); 141 Account oldAcct = (Account) document.getOldMaintainableObject().getBusinessObject(); 142 if (KNSConstants.MAINTENANCE_COPY_ACTION.equals(document.getNewMaintainableObject().getMaintenanceAction())) { 143 if (ObjectUtils.isNull(newAcct.getAccountGuideline())) { 144 newAcct.setAccountGuideline(oldAcct.getAccountGuideline()); 145 } 146 } 147 148 } 149 150 @Override 151 protected void refreshReferences(String referencesToRefresh) { 152 //make call to super 153 super.refreshReferences( removeReferenceFromString(referencesToRefresh, ACCOUNT_GUIDE_LINE_PROPERTY) ); 154 } 155 156 /** 157 * Removes a named reference from a referencesToRefresh string 158 */ 159 protected String removeReferenceFromString(String referencesToRefresh, String referenceToRemove){ 160 String newReference = referencesToRefresh; 161 162 if(ObjectUtils.isNotNull(newReference)){ 163 int index = newReference.indexOf(referenceToRemove); 164 if(index != -1){ 165 //remove from beginning 166 if(index == 0){ 167 168 String suffix = ""; 169 //add comma at end since there is more after this word 170 if(newReference.length() != referenceToRemove.length()){ 171 suffix = ","; 172 } 173 newReference = referencesToRefresh.replaceAll(ACCOUNT_GUIDE_LINE_PROPERTY + suffix, ""); 174 175 }else{ 176 //removing from middle to end... either way, comma will be in front 177 newReference = referencesToRefresh.replaceAll("," + ACCOUNT_GUIDE_LINE_PROPERTY, ""); 178 } 179 } 180 } 181 182 return newReference; 183 } 184 185 /** 186 * @see org.kuali.kfs.sys.document.FinancialSystemMaintainable#populateChartOfAccountsCodeFields() 187 * 188 * Special treatment is needed when a new Account is created, the chartCode-accountNumber fields in the document can use the new account 189 * that's being created; in which case chart code shall be populated from the PK chart code in the document instead of retrieving it from DB 190 * using the account number, as the new account doesn't exist in the DB yet. 191 */ 192 @Override 193 protected void populateChartOfAccountsCodeFields() { 194 // super method is not called because the logic there wouldn't apply here 195 AccountService acctService = SpringContext.getBean(AccountService.class); 196 AccountPersistenceStructureService apsService = SpringContext.getBean(AccountPersistenceStructureService.class); 197 PersistableBusinessObject bo = getBusinessObject(); 198 Iterator<Map.Entry<String, String>> chartAccountPairs = apsService.listChartCodeAccountNumberPairs(bo).entrySet().iterator(); 199 200 // all reference accounts could possibly use the same new accounting being created in the current document 201 while (chartAccountPairs.hasNext()) { 202 Map.Entry<String, String> entry = (Map.Entry<String, String>)chartAccountPairs.next(); 203 String coaCodeName = entry.getKey(); 204 String acctNumName = entry.getValue(); 205 String accountNumber = (String)ObjectUtils.getPropertyValue(bo, acctNumName); 206 String coaCode = null; 207 String coaCodePK = (String)ObjectUtils.getPropertyValue(bo, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE); 208 String accountNumberPK = (String)ObjectUtils.getPropertyValue(bo, KFSPropertyConstants.ACCOUNT_NUMBER); 209 210 // if reference account number is same as the primary key accountNumber, copy the primary key chart code to reference chart Code 211 if (StringUtils.equalsIgnoreCase(accountNumber, accountNumberPK)) { 212 coaCode = coaCodePK; 213 } 214 // otherwise retrieve chart code from account as usual 215 else { 216 Account account = acctService.getUniqueAccountForAccountNumber(accountNumber); 217 if (ObjectUtils.isNotNull(account)) { 218 coaCode = account.getChartOfAccountsCode(); 219 } 220 } 221 222 try { 223 ObjectUtils.setObjectProperty(bo, coaCodeName, coaCode); 224 } 225 catch (Exception e) { 226 LOG.error("Error in setting property value for " + coaCodeName); 227 } 228 } 229 } 230 231 }