/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.coa.document; import java.sql.Date; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.kuali.kfs.coa.businessobject.Account; import org.kuali.kfs.coa.businessobject.IndirectCostRecoveryAccount; import org.kuali.kfs.coa.service.AccountPersistenceStructureService; import org.kuali.kfs.coa.service.AccountService; import org.kuali.kfs.coa.service.SubAccountTrickleDownInactivationService; import org.kuali.kfs.coa.service.SubObjectTrickleDownInactivationService; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.document.FinancialSystemMaintainable; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.kns.document.MaintenanceDocument; import org.kuali.rice.krad.bo.PersistableBusinessObject; import org.kuali.rice.krad.maintenance.MaintenanceLock; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.util.ObjectUtils; /** * This class overrides the saveBusinessObject() method which is called during post process from the KualiPostProcessor so that it * can automatically deactivate the Sub-Accounts related to the account It also overrides the processAfterCopy so that it sets * specific fields that shouldn't be copied to default values {@link KualiPostProcessor} */ public class KualiAccountMaintainableImpl extends FinancialSystemMaintainable { private static final Logger LOG = Logger.getLogger(KualiAccountMaintainableImpl.class); private static final String ACCOUNT_GUIDE_LINE_PROPERTY = "accountGuideline"; /** * Automatically deactivates {@link SubAccount}s after saving the {@link Account} * * @see org.kuali.rice.kns.maintenance.Maintainable#saveBusinessObject() */ @Override public void saveBusinessObject() { boolean isClosingAccount = isClosingAccount(); // make sure we save account first super.saveBusinessObject(); // if we're closing the account, then rely on the trickle-down inactivation services to trickle-down inactivate the // sub-accounts if (isClosingAccount) { SpringContext.getBean(SubAccountTrickleDownInactivationService.class).trickleDownInactivateSubAccounts((Account) getBusinessObject(), getDocumentNumber()); SpringContext.getBean(SubObjectTrickleDownInactivationService.class).trickleDownInactivateSubObjects((Account) getBusinessObject(), getDocumentNumber()); } } /** * After a copy is done set specific fields on {@link Account} to default values * * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterCopy() */ @Override public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) { super.processAfterCopy(document, parameters); Account account = (Account) this.getBusinessObject(); account.setAccountCreateDate(null); // account's pre-rules will fill this field in account.setAccountEffectiveDate(new Date(SpringContext.getBean(DateTimeService.class).getCurrentDate().getTime())); account.setActive(true); List<IndirectCostRecoveryAccount> copyIndirectCostRecoveryAccounts = new ArrayList<IndirectCostRecoveryAccount>(); for(IndirectCostRecoveryAccount indirectAccount : account.getActiveIndirectCostRecoveryAccounts()) { indirectAccount.setIndirectCostRecoveryAccountGeneratedIdentifier(null); copyIndirectCostRecoveryAccounts.add(indirectAccount); } account.setIndirectCostRecoveryAccounts(copyIndirectCostRecoveryAccounts); } @Override public List<MaintenanceLock> generateMaintenanceLocks() { List<MaintenanceLock> maintenanceLocks = super.generateMaintenanceLocks(); boolean isClosingAccount = false; if (isClosingAccount()) { maintenanceLocks.addAll(SpringContext.getBean(SubAccountTrickleDownInactivationService.class).generateTrickleDownMaintenanceLocks((Account) getBusinessObject(), getDocumentNumber())); maintenanceLocks.addAll(SpringContext.getBean(SubObjectTrickleDownInactivationService.class).generateTrickleDownMaintenanceLocks((Account) getBusinessObject(), getDocumentNumber())); } return maintenanceLocks; } protected Account retrieveExistingAccountFromDB() { Account newAccount = (Account) getBusinessObject(); Account oldAccount = SpringContext.getBean(AccountService.class).getByPrimaryId(newAccount.getChartOfAccountsCode(), newAccount.getAccountNumber()); return oldAccount; } protected boolean isClosingAccount() { // 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 if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(getMaintenanceAction()) && !((Account) getBusinessObject()).isActive()) { Account existingAccountFromDB = retrieveExistingAccountFromDB(); if (ObjectUtils.isNotNull(existingAccountFromDB)) { // now see if the original account was not closed, in which case, we are closing the account if (existingAccountFromDB.isActive()) { return true; } } } return false; } /** * Determines who should be FYI'd as the account supervisor for the routing of the account maintenance document. If there is an * existing account, it uses the account supervisor from that; otherwise, it uses the account supervisor from the business * object of this maintainable * * @return an appropriate account supervisor to FYI during account maintenance document routing */ public String getRoutingAccountsSupervisorySystemsIdentifier() { final Account existingAccountFromDB = retrieveExistingAccountFromDB(); if (ObjectUtils.isNull(existingAccountFromDB)) { return ((Account) getBusinessObject()).getAccountsSupervisorySystemsIdentifier(); } return existingAccountFromDB.getAccountsSupervisorySystemsIdentifier(); } /** * Had to override this method because account guideline data was lost when copied and then a lookup is performed * * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#refresh(java.lang.String, java.util.Map, * org.kuali.rice.kns.document.MaintenanceDocument) */ @Override public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) { super.refresh(refreshCaller, fieldValues, document); Account newAcct = (Account) document.getNewMaintainableObject().getBusinessObject(); Account oldAcct = (Account) document.getOldMaintainableObject().getBusinessObject(); if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(document.getNewMaintainableObject().getMaintenanceAction())) { if (ObjectUtils.isNull(newAcct.getAccountGuideline())) { newAcct.setAccountGuideline(oldAcct.getAccountGuideline()); } } } @Override protected void refreshReferences(String referencesToRefresh) { //make call to super super.refreshReferences( removeReferenceFromString(referencesToRefresh, ACCOUNT_GUIDE_LINE_PROPERTY) ); } /** * Removes a named reference from a referencesToRefresh string */ protected String removeReferenceFromString(String referencesToRefresh, String referenceToRemove){ String newReference = referencesToRefresh; if(ObjectUtils.isNotNull(newReference)){ int index = newReference.indexOf(referenceToRemove); if(index != -1){ //remove from beginning if(index == 0){ String suffix = ""; //add comma at end since there is more after this word if(newReference.length() != referenceToRemove.length()){ suffix = ","; } newReference = referencesToRefresh.replaceAll(ACCOUNT_GUIDE_LINE_PROPERTY + suffix, ""); }else{ //removing from middle to end... either way, comma will be in front newReference = referencesToRefresh.replaceAll("," + ACCOUNT_GUIDE_LINE_PROPERTY, ""); } } } return newReference; } /** * @see org.kuali.kfs.sys.document.FinancialSystemMaintainable#populateChartOfAccountsCodeFields() * * Special treatment is needed when a new Account is created, the chartCode-accountNumber fields in the document can use the new account * 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 * using the account number, as the new account doesn't exist in the DB yet. */ @Override protected void populateChartOfAccountsCodeFields() { // super method is not called because the logic there wouldn't apply here AccountService acctService = SpringContext.getBean(AccountService.class); AccountPersistenceStructureService apsService = SpringContext.getBean(AccountPersistenceStructureService.class); PersistableBusinessObject bo = getBusinessObject(); Iterator<Map.Entry<String, String>> chartAccountPairs = apsService.listChartCodeAccountNumberPairs(bo).entrySet().iterator(); // all reference accounts could possibly use the same new accounting being created in the current document while (chartAccountPairs.hasNext()) { Map.Entry<String, String> entry = chartAccountPairs.next(); String coaCodeName = entry.getKey(); String acctNumName = entry.getValue(); String accountNumber = (String)ObjectUtils.getPropertyValue(bo, acctNumName); String coaCode = null; String coaCodePK = (String)ObjectUtils.getPropertyValue(bo, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE); String accountNumberPK = (String)ObjectUtils.getPropertyValue(bo, KFSPropertyConstants.ACCOUNT_NUMBER); // if reference account number is same as the primary key accountNumber, copy the primary key chart code to reference chart Code if (StringUtils.equalsIgnoreCase(accountNumber, accountNumberPK)) { coaCode = coaCodePK; } // otherwise retrieve chart code from account as usual else { Account account = acctService.getUniqueAccountForAccountNumber(accountNumber); if (ObjectUtils.isNotNull(account)) { coaCode = account.getChartOfAccountsCode(); } } try { ObjectUtils.setObjectProperty(bo, coaCodeName, coaCode); } catch (Exception e) { LOG.error("Error in setting property value for " + coaCodeName); } } } }