/*
* Copyright (c) 2005-2011 Grameen Foundation USA
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* See also http://www.apache.org/licenses/LICENSE-2.0.html for an
* explanation of the license and how it is applied.
*/
package org.mifos.config;
import org.mifos.accounts.business.AccountStateEntity;
import org.mifos.accounts.loan.persistance.LegacyLoanDao;
import org.mifos.accounts.persistence.LegacyAccountDao;
import org.mifos.accounts.util.helpers.AccountState;
import org.mifos.application.servicefacade.ApplicationContextProvider;
import org.mifos.config.business.MifosConfigurationManager;
import org.mifos.config.exceptions.ConfigurationException;
import org.mifos.customers.business.CustomerStatusEntity;
import org.mifos.customers.persistence.CustomerDao;
import org.mifos.framework.exceptions.PersistenceException;
import org.mifos.framework.hibernate.helper.StaticHibernateUtil;
/**
* Clients, groups, and accounts have some optional states, which can be hidden
* and excluded from the state flows. If any of the following states are set to
* "false", these optional states will not be visible in the UI. Once an
* optional state is enabled and corresponding loans, savings accounts, clients,
* etc. have been created, it should not be disabled as records could be in an
* optional state and unmodifiable from the Mifos user interface.
* <p>
* Overriding to "false" in the custom configuration file must be performed
* before any loans, savings accounts, or clients (depending on the setting)
* have been created.
* <p>
* Values in the database are <em>not used during runtime.</em> Rather, they are
* a weak attempt at tracking state to try to avoid an inappropriate flag
* override in the config file.
*/
public class ProcessFlowRules {
private static final String SAVINGS_PENDING_APPROVAL = "ProcessFlow.SavingsPendingApprovalStateEnabled";
private static final String LOAN_PENDING_APPROVAL = "ProcessFlow.LoanPendingApprovalStateEnabled";
private static final String GROUP_PENDING_APPROVAL = "ProcessFlow.GroupPendingApprovalStateEnabled";
private static final String CLIENT_PENDING_APPROVAL = "ProcessFlow.ClientPendingApprovalStateEnabled";
/**
* Performs startup sanity checks. While not a requirement, it is considered
* good practice to call this method prior to any other methods in this
* class. This method is only intended to be used during startup, ie: in a
* single thread.
*/
public static void init() throws PersistenceException, ConfigurationException {
try {
initClientPendingApprovalState();
initGroupPendingApprovalState();
initLoanPendingApprovalState();
initSavingsPendingApprovalState();
StaticHibernateUtil.commitTransaction();
} catch (ConfigurationException ce) {
StaticHibernateUtil.rollbackTransaction();
throw ce;
}
}
public static void initFromDB() {
MifosConfigurationManager cm = MifosConfigurationManager.getInstance();
CustomerDao customerDao = ApplicationContextProvider.getBean(CustomerDao.class);
LegacyAccountDao ap = ApplicationContextProvider.getBean(LegacyAccountDao.class);
CustomerStatusEntity cse = customerDao.findClientPendingStatus();
cm.setProperty(CLIENT_PENDING_APPROVAL, isClientPendingApprovalStateEnabledOnDatabaseConfiguration(cse));
cse = customerDao.findGroupPendingStatus();
cm.setProperty(GROUP_PENDING_APPROVAL, isGroupPendingApprovalStateEnabledOnDatabaseConfiguration(cse));
AccountStateEntity ase = ap.loadPersistentObject(AccountStateEntity.class, AccountState.LOAN_PENDING_APPROVAL.getValue());
cm.setProperty(LOAN_PENDING_APPROVAL, isLoanPendingApprovalStateEnabledOnDatabaseConfig(ase));
ase = ap.loadPersistentObject(AccountStateEntity.class, AccountState.SAVINGS_PENDING_APPROVAL.getValue());
cm.setProperty(SAVINGS_PENDING_APPROVAL, isSavingPendingApprovalStateEnabledOnDatabaseConfig(ase));
}
private static String getBadOverrideMsg(String key, String detailMsg) {
return "The value for key " + key + " in the file " + MifosConfigurationManager.CUSTOM_CONFIG_PROPS_FILENAME
+ " must to be set to 1 because it was set to 1"
+ " in the database, hence can't be set to to 0 in the custom"
+ " configuration file as this might invalidate existing data. " + detailMsg + " Also, "
+ MifosConfigurationManager.DEFAULT_CONFIG_PROPS_FILENAME + " must never be changed--make sure this"
+ " file is untouched.";
}
/**
* The only disallowed combination of database to config file override of a
* process flow optional state flag is from true to false.
*/
// "protected" visibility so it can be unit tested; it would otherwise
// be private
protected static boolean isValidOverride(boolean fromDb, boolean fromCfg) {
if (fromDb && !fromCfg) {
return false;
}
return true;
}
/**
* Given the setting of a process flow optional state flag in the database
* and in the config file, determine if it is necessary to override the
* value stored in the database. Uses logic similar to ClientRules: override
* is necessary when flag in db is false and flag in config file is true.
*
* @throws ConfigurationException
* if an invalid override is specified. Will not be thrown if
* arguments pass {@link #isValidOverride(boolean, boolean)}.
*/
// "protected" visibility so it can be unit tested; it would otherwise
// be private
protected static boolean needsOverride(boolean fromDb, boolean fromCfg) throws ConfigurationException {
if (fromDb && fromCfg) {
return false;
} else if (!fromDb && !fromCfg) {
return false;
} else if (!fromDb && fromCfg) {
return true;
}
throw new ConfigurationException("unexpected override specified [" + fromDb + "], [" + fromCfg + "]");
}
private static void initClientPendingApprovalState() throws ConfigurationException {
CustomerDao customerDao = ApplicationContextProvider.getBean(CustomerDao.class);
CustomerStatusEntity cse = customerDao.findClientPendingStatus();
boolean fromDb = isClientPendingApprovalStateEnabledOnDatabaseConfiguration(cse);
boolean fromCfg = isClientPendingApprovalStateEnabled();
if (databaseAndCustomConfigurationAreNotTheSame(fromDb, fromCfg)) {
int count = customerDao.countOfClients();
if (count > 0) {
final String errMsg = getBadOverrideMsg(CLIENT_PENDING_APPROVAL,
"Records for clients in the 'pending approval' state" + " may already exist.");
throw new ConfigurationException(errMsg);
}
makeDatabaseConfigurationMatchPropertiesFileConfiguration(customerDao, cse, fromCfg);
}
}
private static boolean isClientPendingApprovalStateEnabledOnDatabaseConfiguration(CustomerStatusEntity cse) {
return cse.getIsOptional();
}
private static void makeDatabaseConfigurationMatchPropertiesFileConfiguration(CustomerDao customerDao,
CustomerStatusEntity cse, boolean fromCfg) {
cse.setIsOptional(fromCfg);
StaticHibernateUtil.startTransaction();
customerDao.save(cse);
StaticHibernateUtil.commitTransaction();
}
private static boolean databaseAndCustomConfigurationAreNotTheSame(boolean fromDb, boolean fromCfg) {
return !Boolean.valueOf(fromDb).equals(Boolean.valueOf(fromCfg));
}
public static boolean isClientPendingApprovalStateEnabled() {
MifosConfigurationManager cm = MifosConfigurationManager.getInstance();
return cm.getBoolean(CLIENT_PENDING_APPROVAL);
}
private static void initGroupPendingApprovalState() throws ConfigurationException {
CustomerDao customerDao = ApplicationContextProvider.getBean(CustomerDao.class);
CustomerStatusEntity cse = customerDao.findGroupPendingStatus();
boolean fromDb = isGroupPendingApprovalStateEnabledOnDatabaseConfiguration(cse);
boolean fromCfg = isGroupPendingApprovalStateEnabled();
if (databaseAndCustomConfigurationAreNotTheSame(fromDb, fromCfg)) {
int count = customerDao.countOfGroups();
if (count > 0) {
final String errMsg = getBadOverrideMsg(GROUP_PENDING_APPROVAL, "Records for groups in the 'pending approval' state"
+ " may already exist.");
throw new ConfigurationException(errMsg);
}
makeDatabaseConfigurationMatchPropertiesFileConfiguration(customerDao, cse, fromCfg);
}
}
private static boolean isGroupPendingApprovalStateEnabledOnDatabaseConfiguration(CustomerStatusEntity cse) {
return cse.getIsOptional();
}
public static boolean isGroupPendingApprovalStateEnabled() {
MifosConfigurationManager cm = MifosConfigurationManager.getInstance();
return cm.getBoolean(GROUP_PENDING_APPROVAL);
}
private static void initLoanPendingApprovalState() throws ConfigurationException {
LegacyAccountDao ap = ApplicationContextProvider.getBean(LegacyAccountDao.class);
AccountStateEntity ase = ap.loadPersistentObject(AccountStateEntity.class, AccountState.LOAN_PENDING_APPROVAL.getValue());
boolean fromDb = isLoanPendingApprovalStateEnabledOnDatabaseConfig(ase);
boolean fromCfg = isLoanPendingApprovalStateEnabled();
if (databaseAndCustomConfigurationAreNotTheSame(fromDb, fromCfg)) {
int count = ApplicationContextProvider.getBean(LegacyLoanDao.class).countOfLoanAccounts();
if (count > 0) {
String errMsg = getBadOverrideMsg(LOAN_PENDING_APPROVAL, "Records for loans in the 'pending approval' state"
+ " may already exist.");
throw new ConfigurationException(errMsg);
}
makeDatabaseConfigurationMatchPropertiesFileConfiguration(ap, ase, fromCfg);
}
}
private static void makeDatabaseConfigurationMatchPropertiesFileConfiguration(LegacyAccountDao persistence,
AccountStateEntity entity, boolean fromCfg) {
entity.setIsOptional(fromCfg);
try {
persistence.createOrUpdate(entity);
StaticHibernateUtil.commitTransaction();
} catch (PersistenceException e) {
StaticHibernateUtil.rollbackTransaction();
} finally {
StaticHibernateUtil.closeSession();
}
}
private static boolean isSavingPendingApprovalStateEnabledOnDatabaseConfig(AccountStateEntity ase) {
return ase.getIsOptional();
}
private static boolean isLoanPendingApprovalStateEnabledOnDatabaseConfig(AccountStateEntity ase) {
return ase.getIsOptional();
}
public static boolean isLoanPendingApprovalStateEnabled() {
MifosConfigurationManager cm = MifosConfigurationManager.getInstance();
return cm.getBoolean(LOAN_PENDING_APPROVAL);
}
private static void initSavingsPendingApprovalState() throws ConfigurationException {
LegacyAccountDao ap = ApplicationContextProvider.getBean(LegacyAccountDao.class);
AccountStateEntity ase = ap.loadPersistentObject(AccountStateEntity.class, AccountState.SAVINGS_PENDING_APPROVAL.getValue());
boolean fromDb = isSavingPendingApprovalStateEnabledOnDatabaseConfig(ase);
boolean fromCfg = isSavingsPendingApprovalStateEnabled();
if (databaseAndCustomConfigurationAreNotTheSame(fromDb, fromCfg)) {
int count = ApplicationContextProvider.getBean(LegacyLoanDao.class).countOfSavingsAccounts();
if (count > 0) {
String errMsg = getBadOverrideMsg(SAVINGS_PENDING_APPROVAL,
"Records for savings accounts in the 'pending approval' state" + " may already exist.");
throw new ConfigurationException(errMsg);
}
makeDatabaseConfigurationMatchPropertiesFileConfiguration(ap, ase, fromCfg);
}
}
public static boolean isSavingsPendingApprovalStateEnabled() {
MifosConfigurationManager cm = MifosConfigurationManager.getInstance();
return cm.getBoolean(SAVINGS_PENDING_APPROVAL);
}
}