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 }