Java tutorial
/* * 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 java.math.BigDecimal; import java.math.RoundingMode; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang.StringUtils; import org.mifos.application.master.business.MifosCurrency; import org.mifos.config.business.MifosConfigurationManager; import org.mifos.config.persistence.ConfigurationPersistence; import org.mifos.core.MifosRuntimeException; public class AccountingRules { // if you change any of the following values please change the test cases to // match these values // if any of these configured entries are not defined in the application // config file they will get these values private static final BigDecimal DEFAULT_INITIAL_ROUNDOFF_MULTIPLE = new BigDecimal("1"); private static final BigDecimal DEFAULT_FINAL_ROUNDOFF_MULTIPLE = new BigDecimal("1"); private static final RoundingMode DEFAULT_INITIAL_ROUNDING_MODE = RoundingMode.HALF_UP; private static final RoundingMode DEFAULT_FINAL_ROUNDING_MODE = RoundingMode.CEILING; private static final RoundingMode DEFAULT_CURRENCY_ROUNDING_MODE = RoundingMode.HALF_UP; /** * An internal property which represents the digits before decimal of an amount that can be entered through * any User Interface, due to <b>MySQL DECIMAL(21,4)</b> for amount field in database we can store a value of up to 17 * digits, but some of them are totals. * <br /><br /> * To make sure that totals will not overflow we have allowed 14 as the limit. * * for details see http://mifosforge.jira.com/browse/MIFOS-1537 */ private static final Short DIGITS_BEFORE_DECIMAL_FOR_AMOUNT = 14; /** * An internal property which represents the digits before decimal of an interest that can be entered through * any User Interface, due to <b>MySQL DECIMAL(13,10)</b> for interest field in database we can store a value of up to 13 * digits, but some of them are totals. * <br /><br /> * To make sure that totals will not overflow we have allowed 10 as the limit. * * for details see http://mifosforge.jira.com/browse/MIFOS-1537 */ private static final Short DIGITS_BEFORE_DECIMAL_FOR_INTEREST = 10; private static final Short DIGITS_BEFORE_DECIMAL_FOR_CASH_FLOW_VALIDATIONS = 10; // FIXME: we should use a standard caching mechanism rather than ad hoc caches like // this. Also, we need to consider if this should be thread safe since this initial // implementation is not thread safe for initialization. Re-initialization should // only happen for test cases, so that most likely is okay. Adding some // synchronization could make it thread safe, but this will be accessed every time // a non-default currency is read from the database, so care needs to be taken // regarding performance. private static final LinkedList<MifosCurrency> currencies = new LinkedList<MifosCurrency>(); /* * Allow for reloading the currencies if the configuration has been changed during testing. */ public static void init() { currencies.clear(); getCurrencies(); } public static MifosCurrency getMifosCurrency(ConfigurationPersistence configurationPersistence) { return getMifosCurrency(getDefaultCurrencyCode(), configurationPersistence); } public static MifosCurrency getMifosCurrency(String currencyCode, ConfigurationPersistence configurationPersistence) { MifosCurrency currency = configurationPersistence.getCurrency(currencyCode); if (currency == null) { throw new RuntimeException( "Can't find in the database the currency define in the config file " + currencyCode); } Short digitsAfterDecimal = getDigitsAfterDecimal(currency); BigDecimal amountToBeRoundedTo = getAmountToBeRoundedTo(currency.getRoundingAmount()); return new MifosCurrency(currency.getCurrencyId(), currency.getCurrencyName(), amountToBeRoundedTo, currencyCode); } /** * * Gets the List of currencies configured to use in Mifos, * the first element will be the default currency. * * @return List of currencies */ public static LinkedList<MifosCurrency> getCurrencies() { if (currencies.size() == 0) { currencies.add(AccountingRules.getMifosCurrency(new ConfigurationPersistence())); ConfigurationPersistence configurationPersistence = new ConfigurationPersistence(); for (String currencyCode : AccountingRules.getAdditionalCurrencyCodes()) { currencies.add(getMifosCurrency(currencyCode, configurationPersistence)); } } return currencies; } /** * Gets the currency by currency id from the list of currencies configured to used in Mifos * {@link AccountingRules#getCurrencies()} * * @param currencyId * @return {@link MifosCurrency} */ public static MifosCurrency getCurrencyByCurrencyId(Short currencyId) { LinkedList<MifosCurrency> currencies = getCurrencies(); Iterator<MifosCurrency> i = currencies.iterator(); while (i.hasNext()) { MifosCurrency a = i.next(); if (a.getCurrencyId().equals(currencyId)) { return a; } } throw new MifosRuntimeException("Unable to find currency with id: " + currencyId + ". You may be missing an entry for the currency with this id in " + MifosConfigurationManager.CUSTOM_CONFIG_PROPS_FILENAME + "."); } public static String getDefaultCurrencyCode() { return MifosConfigurationManager.getInstance().getString(AccountingRulesConstants.CURRENCY_CODE); } /* * suppress unchecked casts to allow genericized List<String> to be used by * callers */ @SuppressWarnings("unchecked") public static List<String> getAdditionalCurrencyCodes() { return MifosConfigurationManager.getInstance().getList(AccountingRulesConstants.ADDITIONAL_CURRENCY_CODES); } public static Double getMaxInterest() { return MifosConfigurationManager.getInstance().getDouble(AccountingRulesConstants.MAX_INTEREST); } public static Double getMinInterest() { return MifosConfigurationManager.getInstance().getDouble(AccountingRulesConstants.MIN_INTEREST); } public static Double getMaxCashFlowThreshold() { return MifosConfigurationManager.getInstance().getDouble(AccountingRulesConstants.MAX_CASH_FLOW_THRESHOLD); } public static Double getMinCashFlowThreshold() { return MifosConfigurationManager.getInstance().getDouble(AccountingRulesConstants.MIN_CASH_FLOW_THRESHOLD); } public static Double getMaxIndebtednessRatio() { return MifosConfigurationManager.getInstance().getDouble(AccountingRulesConstants.MAX_INDEBTEDNESS_RATIO); } public static Double getMinIndebtednessRatio() { return MifosConfigurationManager.getInstance().getDouble(AccountingRulesConstants.MIN_INDEBTEDNESS_RATIO); } public static Double getMaxRepaymentCapacity() { return MifosConfigurationManager.getInstance().getDouble(AccountingRulesConstants.MAX_REPAYMENT_CAPACITY); } public static Double getMinRepaymentCapacity() { return MifosConfigurationManager.getInstance().getDouble(AccountingRulesConstants.MIN_REPAYMENT_CAPACITY); } public static Short getDigitsAfterDecimal() { return MifosConfigurationManager.getInstance().getShort(AccountingRulesConstants.DIGITS_AFTER_DECIMAL); } public static Short getDigitsAfterDecimal(final MifosCurrency currency) { if (currency == null) return getDigitsAfterDecimal(); final String code = currency.getCurrencyCode(); if (getDefaultCurrencyCode().equals(code)) { return getDigitsAfterDecimal(); } if (!getAdditionalCurrencyCodes().contains(code)) { throw new IllegalArgumentException(String.format("Currency not configured. %s. Default currency is: %s", currency, getDefaultCurrencyCode())); } return MifosConfigurationManager.getInstance() .getShort(AccountingRulesConstants.DIGITS_AFTER_DECIMAL + "." + code, getDigitsAfterDecimal()); } public static Short getDigitsBeforeDecimal() { return DIGITS_BEFORE_DECIMAL_FOR_AMOUNT; } public static Short getDigitsBeforeDecimalForInterest() { return DIGITS_BEFORE_DECIMAL_FOR_INTEREST; } public static Short getDigitsAfterDecimalForInterest() { return MifosConfigurationManager.getInstance() .getShort(AccountingRulesConstants.DIGITS_AFTER_DECIMAL_FOR_INTEREST); } public static Short getDigitsBeforeDecimalForCashFlowValidations() { return DIGITS_BEFORE_DECIMAL_FOR_CASH_FLOW_VALIDATIONS; } public static Short getDigitsAfterDecimalForCashFlowValidations() { return MifosConfigurationManager.getInstance() .getShort(AccountingRulesConstants.DIGITS_AFTER_DECIMAL_FOR_CASHFLOW_VALIDATIONS); } // the defaultValue passed in should be the value from database public static BigDecimal getAmountToBeRoundedTo(BigDecimal defaultValue) { return MifosConfigurationManager.getInstance() .getBigDecimal(AccountingRulesConstants.AMOUNT_TO_BE_ROUNDED_TO, defaultValue); } public static Short getRoundingMode(Short defaultValue) { Short mode; MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); if (configMgr.containsKey(AccountingRulesConstants.ROUNDING_RULE)) { String returnStr = configMgr.getString(AccountingRulesConstants.ROUNDING_RULE); if (returnStr.equals("FLOOR")) { mode = MifosCurrency.FLOOR_MODE; } else if (returnStr.equals("CEILING")) { mode = MifosCurrency.CEILING_MODE; } else if (returnStr.equals("HALF_UP")) { mode = MifosCurrency.HALF_UP_MODE; } else { throw new RuntimeException( "The rounding mode defined in the config file is not CEILING, FLOOR, HALF_UP. It is " + returnStr); } } else { mode = defaultValue; } return mode; } /* * Expected to return either 360 or 365 */ public static Short getNumberOfInterestDays() { Short days = MifosConfigurationManager.getInstance() .getShort(AccountingRulesConstants.NUMBER_OF_INTEREST_DAYS); if ((days != 365) && (days != 360)) { throw new RuntimeException("Invalid number of interest days defined in property file " + days); } return days; } /** * Head Office can specify whether/not system will accept back-dated * transactions. This is an MFI-wide setting and will be applicable to all * transactions in all offices for all loans, savings and client accounts. * By default, backdated transactions should be allowed. If the setting is * changed it only applies to future transactions * <ul> * <li>If "true", user can enter transactions dated earlier than current * date (but later than last meeting date).</li> * <li>If "false", user can only enter transactions dated with the current * date. Also, "date of transaction" for bulk entry will always be the * current date.</li> * </ul> */ public static boolean isBackDatedTxnAllowed() { MifosConfigurationManager cm = MifosConfigurationManager.getInstance(); return cm.getBoolean(AccountingRulesConstants.BACKDATED_TRANSACTIONS_ALLOWED); } public static Boolean isMultiCurrencyEnabled() { if (getAdditionalCurrencyCodes().isEmpty()) { return false; } return true; } public static RoundingMode getInitialRoundingMode() { MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); String modeStr = configMgr.getString(AccountingRulesConstants.INITIAL_ROUNDING_MODE); return getRoundingModeFromString(modeStr, "InitialRoundingMode", DEFAULT_INITIAL_ROUNDING_MODE); } private static RoundingMode getRoundingModeFromString(String modeStr, String type, RoundingMode defaultRoundingMode) { if (StringUtils.isBlank(modeStr)) { return defaultRoundingMode; } RoundingMode mode = null; if (modeStr.equals("FLOOR")) { mode = RoundingMode.FLOOR; } else if (modeStr.equals("CEILING")) { mode = RoundingMode.CEILING; } else if (modeStr.equals("HALF_UP")) { mode = RoundingMode.HALF_UP; } else { throw new RuntimeException( type + " defined in the config file is not CEILING, FLOOR, HALF_UP. It is " + modeStr); } return mode; } public static RoundingMode getFinalRoundingMode() { MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); String modeStr = configMgr.getString(AccountingRulesConstants.FINAL_ROUNDING_MODE); return getRoundingModeFromString(modeStr, "FinalRoundingMode", DEFAULT_FINAL_ROUNDING_MODE); } private static BigDecimal getRoundOffMultipleFromString(String roundOffStr, BigDecimal defaultRoundOffMultiple) { if (StringUtils.isBlank(roundOffStr)) { return defaultRoundOffMultiple; } return new BigDecimal(roundOffStr); } public static BigDecimal getInitialRoundOffMultiple() { MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); String modeStr = configMgr.getString(AccountingRulesConstants.INITIAL_ROUND_OFF_MULTIPLE); return getRoundOffMultipleFromString(modeStr, DEFAULT_INITIAL_ROUNDOFF_MULTIPLE); } public static BigDecimal getInitialRoundOffMultiple(final MifosCurrency currency) { final String code = currency.getCurrencyCode(); if (getDefaultCurrencyCode().equals(code)) { return getInitialRoundOffMultiple(); } if (!getAdditionalCurrencyCodes().contains(code)) { throw new IllegalArgumentException("Currency not configured. " + currency); } String modeStr = MifosConfigurationManager.getInstance() .getString(AccountingRulesConstants.INITIAL_ROUND_OFF_MULTIPLE + "." + code); return getRoundOffMultipleFromString(modeStr, DEFAULT_INITIAL_ROUNDOFF_MULTIPLE); } public static BigDecimal getFinalRoundOffMultiple() { MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); String modeStr = configMgr.getString(AccountingRulesConstants.FINAL_ROUND_OFF_MULTIPLE); return getRoundOffMultipleFromString(modeStr, DEFAULT_FINAL_ROUNDOFF_MULTIPLE); } public static BigDecimal getFinalRoundOffMultiple(final MifosCurrency currency) { final String code = currency.getCurrencyCode(); if (getDefaultCurrencyCode().equals(code)) { return getFinalRoundOffMultiple(); } if (!getAdditionalCurrencyCodes().contains(code)) { throw new IllegalArgumentException("Currency not configured. " + currency); } String modeStr = MifosConfigurationManager.getInstance() .getString(AccountingRulesConstants.FINAL_ROUND_OFF_MULTIPLE + "." + code); return getRoundOffMultipleFromString(modeStr, DEFAULT_FINAL_ROUNDOFF_MULTIPLE); } public static RoundingMode getCurrencyRoundingMode() { MifosConfigurationManager configMgr = MifosConfigurationManager.getInstance(); String modeStr = configMgr.getString(AccountingRulesConstants.CURRENCY_ROUNDING_MODE); return getRoundingModeFromString(modeStr, "CurrencyRoundingMode", DEFAULT_CURRENCY_ROUNDING_MODE); } /* * Return a decimal corresponding to the number of digits after the decimal. * For example 2 digits after the decimal should map to 0.01, one digit to * 0.1 */ public static BigDecimal getDigitsAfterDecimalMultiple(MifosCurrency currency) { int digitsAfterDecimal = getDigitsAfterDecimal(currency).intValue(); if (digitsAfterDecimal >= 0) { BigDecimal divisor = new BigDecimal("10").pow(digitsAfterDecimal); return new BigDecimal("1").divide(divisor); } return new BigDecimal("10").pow(-digitsAfterDecimal); } public static void setDigitsAfterDecimal(Short value) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.DIGITS_AFTER_DECIMAL, value); } public static void setDigitsAfterDecimalForInterest(Short value) { MifosConfigurationManager.getInstance() .setProperty(AccountingRulesConstants.DIGITS_AFTER_DECIMAL_FOR_INTEREST, value); } public static void setRoundingRule(RoundingMode mode) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.ROUNDING_RULE, mode.name()); } public static void setFinalRoundOffMultiple(BigDecimal finalRoundOffMultiple) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.FINAL_ROUND_OFF_MULTIPLE, finalRoundOffMultiple.toString()); } public static void setInitialRoundOffMultiple(BigDecimal initialRoundOffMultiple) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.INITIAL_ROUND_OFF_MULTIPLE, initialRoundOffMultiple.toString()); } public static void setCurrencyRoundingMode(RoundingMode currencyRoundingMode) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.CURRENCY_ROUNDING_MODE, currencyRoundingMode.name()); } public static void setInitialRoundingMode(RoundingMode intialRoundingMode) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.INITIAL_ROUNDING_MODE, intialRoundingMode.name()); } public static void setFinalRoundingMode(RoundingMode finalRoundingMode) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.FINAL_ROUNDING_MODE, finalRoundingMode.name()); } public static void setBackDatedTransactionsAllowed(Boolean value) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.BACKDATED_TRANSACTIONS_ALLOWED, value); } public static void setMaxInterest(BigDecimal maxInterest) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.MAX_INTEREST, maxInterest.toString()); } public static void setMinInterest(BigDecimal minInterest) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.MIN_INTEREST, minInterest.toString()); } public static void setNumberOfInterestDays(Integer numberOfInterestDays) { MifosConfigurationManager.getInstance().setProperty(AccountingRulesConstants.NUMBER_OF_INTEREST_DAYS, numberOfInterestDays); } }