MIFOSX-1394 overdraft account interest calculation fix
This commit is contained in:
+139
@@ -3,6 +3,8 @@ package org.mifosplatform.integrationtests;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
@@ -479,15 +481,152 @@ public class ClientSavingsIntegrationTest {
|
||||
assertEquals(expectedNextDueDate, nextDueDate);
|
||||
}
|
||||
|
||||
/***
|
||||
* Test case for overdraft account functionality. Open account with zero
|
||||
* balance, perform transactions then post interest and verify posted
|
||||
* interest
|
||||
*/
|
||||
@Test
|
||||
public void testSavingsAccountWithOverdraft() {
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
/***
|
||||
* Create a client to apply for savings account (overdraft account).
|
||||
*/
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
Assert.assertNotNull(clientID);
|
||||
final String minBalanceForInterestCalculation = null;
|
||||
|
||||
/***
|
||||
* Create savings product with zero opening balance and overdraft
|
||||
* enabled
|
||||
*/
|
||||
final String zeroOpeningBalance = "0.0";
|
||||
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, zeroOpeningBalance,
|
||||
minBalanceForInterestCalculation);
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
/***
|
||||
* Apply for Savings account
|
||||
*/
|
||||
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
HashMap modifications = this.savingsAccountHelper.updateSavingsAccount(clientID, savingsProductID, savingsId,
|
||||
ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assert.assertTrue(modifications.containsKey("submittedOnDate"));
|
||||
|
||||
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
|
||||
|
||||
/***
|
||||
* Approve the savings account
|
||||
*/
|
||||
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
|
||||
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
|
||||
Calendar todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -1);
|
||||
todaysDate.set(Calendar.DAY_OF_MONTH, 1);
|
||||
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final Integer lastDayOfMonth = todaysDate.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
todaysDate.set(Calendar.DAY_OF_MONTH, lastDayOfMonth);
|
||||
final String TRANSACTION_DATE = dateFormat.format(todaysDate.getTime());
|
||||
|
||||
/***
|
||||
* Activate the application and verify account status
|
||||
*
|
||||
* @param activationDate
|
||||
* this value is every time first day of previous month
|
||||
*/
|
||||
savingsStatusHashMap = activateSavingsAccount(savingsId, ACTIVATION_DATE);
|
||||
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
|
||||
|
||||
/***
|
||||
* Verify the account summary
|
||||
*/
|
||||
final HashMap summaryBefore = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
this.savingsAccountHelper.calculateInterestForSavings(savingsId);
|
||||
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
assertEquals(summaryBefore, summary);
|
||||
|
||||
Float balance = Float.valueOf(zeroOpeningBalance);
|
||||
|
||||
/***
|
||||
* Perform withdraw transaction, verify account balance(account balance
|
||||
* will go to negative as no deposits are there prior to this
|
||||
* transaction)
|
||||
*/
|
||||
Integer withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, WITHDRAW_AMOUNT,
|
||||
ACTIVATION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
|
||||
HashMap withdrawTransaction = this.savingsAccountHelper.getSavingsTransaction(savingsId, withdrawTransactionId);
|
||||
balance -= new Float(WITHDRAW_AMOUNT);
|
||||
assertEquals("Verifying Withdrawal Amount", new Float(WITHDRAW_AMOUNT), withdrawTransaction.get("amount"));
|
||||
assertEquals("Verifying Balance after Withdrawal", balance, withdrawTransaction.get("runningBalance"));
|
||||
|
||||
/***
|
||||
* Perform Deposit transaction on last day of month and verify account
|
||||
* balance.
|
||||
*
|
||||
* @param transactionDate
|
||||
* this value is every time last day of previous month
|
||||
*/
|
||||
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, DEPOSIT_AMOUNT,
|
||||
TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
|
||||
HashMap depositTransaction = this.savingsAccountHelper.getSavingsTransaction(savingsId, depositTransactionId);
|
||||
balance += new Float(DEPOSIT_AMOUNT);
|
||||
assertEquals("Verifying Deposit Amount", new Float(DEPOSIT_AMOUNT), depositTransaction.get("amount"));
|
||||
assertEquals("Verifying Balance after Deposit", balance, depositTransaction.get("runningBalance"));
|
||||
|
||||
/***
|
||||
* Perform Post interest transaction and verify the posted amount
|
||||
*/
|
||||
this.savingsAccountHelper.postInterestForSavings(savingsId);
|
||||
HashMap accountDetails = this.savingsAccountHelper.getSavingsDetails(savingsId);
|
||||
summary = (HashMap) accountDetails.get("summary");
|
||||
Float actualInterestPosted = Float.valueOf(summary.get("totalInterestPosted").toString());
|
||||
|
||||
/***
|
||||
* Calculate expected interest to be posted, interest should be posted
|
||||
* for one day only because deposit transaction happened on last day of
|
||||
* month before this account balance is negative.
|
||||
*/
|
||||
final Float nominalAnnualInterest = Float.valueOf(accountDetails.get("nominalAnnualInterestRate").toString());
|
||||
final HashMap interestCalculationDaysInYearType = (HashMap) accountDetails.get("interestCalculationDaysInYearType");
|
||||
final Integer daysInYear = Integer.valueOf(interestCalculationDaysInYearType.get("id").toString());
|
||||
double interestRateInFraction = (nominalAnnualInterest / 100);
|
||||
double perDay = (double) 1 / (daysInYear);
|
||||
double interestPerDay = interestRateInFraction * perDay;
|
||||
Float interestPosted = (float) (interestPerDay * balance * 1);
|
||||
|
||||
/***
|
||||
* Apply rounding on interestPosted, actualInterestPosted and verify
|
||||
* both are same
|
||||
*/
|
||||
DecimalFormat decimalFormat = new DecimalFormat();
|
||||
decimalFormat.applyPattern("#.###");
|
||||
interestPosted = new Float(decimalFormat.format(interestPosted));
|
||||
actualInterestPosted = new Float(decimalFormat.format(actualInterestPosted));
|
||||
assertEquals("Verifying interest posted", interestPosted, actualInterestPosted);
|
||||
}
|
||||
|
||||
private HashMap activateSavingsAccount(final Integer savingsId, final String activationDate) {
|
||||
final HashMap status = this.savingsAccountHelper.activateSavingsAccount(savingsId, activationDate);
|
||||
return status;
|
||||
}
|
||||
|
||||
private Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String minOpenningBalance, String minBalanceForInterestCalculation) {
|
||||
System.out.println("------------------------------CREATING NEW SAVINGS PRODUCT ---------------------------------------");
|
||||
SavingsProductHelper savingsProductHelper = new SavingsProductHelper();
|
||||
final String overDraftLimit = "2000.0";
|
||||
final String savingsProductJSON = savingsProductHelper //
|
||||
.withInterestCompoundingPeriodTypeAsDaily() //
|
||||
.withInterestPostingPeriodTypeAsMonthly() //
|
||||
.withInterestCalculationPeriodTypeAsDailyBalance() //
|
||||
.withMinBalanceForInterestCalculation(minBalanceForInterestCalculation)//
|
||||
.withOverDraft(overDraftLimit)
|
||||
.withMinimumOpenningBalance(minOpenningBalance).build();
|
||||
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
|
||||
}
|
||||
|
||||
+19
@@ -329,6 +329,12 @@ public class SavingsAccountHelper {
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, "summary");
|
||||
return response;
|
||||
}
|
||||
|
||||
public HashMap getSavingsDetails(final Integer savingsID) {
|
||||
final String URL = SAVINGS_ACCOUNT_URL + "/" + savingsID + "?associations=all&" + Utils.TENANT_IDENTIFIER;
|
||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, "");
|
||||
return response;
|
||||
}
|
||||
|
||||
private HashMap performSavingApplicationActions(final String postURLForSavingsTransaction, final String jsonToBeSent) {
|
||||
HashMap status = null;
|
||||
@@ -358,4 +364,17 @@ public class SavingsAccountHelper {
|
||||
String json = new Gson().toJson(map);
|
||||
return json;
|
||||
}
|
||||
|
||||
private String getAccountActivationJSON(final String activationDate) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
map.put("locale", CommonConstants.locale);
|
||||
map.put("dateFormat", CommonConstants.dateFormat);
|
||||
map.put("activatedOnDate", activationDate);
|
||||
String savingsAccountActivateJson = new Gson().toJson(map);
|
||||
return savingsAccountActivateJson;
|
||||
}
|
||||
|
||||
public HashMap activateSavingsAccount(Integer savingsID, String activationDate) {
|
||||
return performSavingApplicationActions(createSavingsOperationURL(ACTIVATE_SAVINGS_COMMAND, savingsID), getAccountActivationJSON(activationDate));
|
||||
}
|
||||
}
|
||||
+10
@@ -64,6 +64,8 @@ public class SavingsProductHelper {
|
||||
private final String interestCalculationDaysInYearType = DAYS_365;
|
||||
private Account[] accountList = null;
|
||||
private String minBalanceForInterestCalculation = null;
|
||||
private String allowOverdraft = "false";
|
||||
private String overdraftLimit = null;
|
||||
|
||||
public String build() {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
@@ -95,6 +97,8 @@ public class SavingsProductHelper {
|
||||
map.put("lockinPeriodFrequencyType", this.lockingPeriodFrequencyType);
|
||||
map.put("withdrawalFeeForTransfers", this.withdrawalFeeForTransfers);
|
||||
map.put("minBalanceForInterestCalculation", minBalanceForInterestCalculation);
|
||||
map.put("allowOverdraft", this.allowOverdraft);
|
||||
map.put("overdraftLimit", this.overdraftLimit);
|
||||
|
||||
if (this.accountingRule.equals(CASH_BASED)) {
|
||||
map.putAll(getAccountMappingForCashBased());
|
||||
@@ -165,6 +169,12 @@ public class SavingsProductHelper {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SavingsProductHelper withOverDraft(final String overDraftLimit) {
|
||||
this.allowOverdraft = "true";
|
||||
this.overdraftLimit = overDraftLimit;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Map<String, String> getAccountMappingForCashBased() {
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
if (accountList != null) {
|
||||
|
||||
+8
-1
@@ -212,9 +212,16 @@ public class SavingsAccountAssembler {
|
||||
boolean allowOverdraft = false;
|
||||
if (command.parameterExists(allowOverdraftParamName)) {
|
||||
allowOverdraft = command.booleanPrimitiveValueOfParameterNamed(allowOverdraftParamName);
|
||||
} else {
|
||||
allowOverdraft = product.isAllowOverdraft();
|
||||
}
|
||||
|
||||
final BigDecimal overdraftLimit = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(overdraftLimitParamName);
|
||||
BigDecimal overdraftLimit = null;
|
||||
if (command.parameterExists(overdraftLimitParamName)) {
|
||||
overdraftLimit = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(overdraftLimitParamName);
|
||||
} else {
|
||||
overdraftLimit = product.overdraftLimit();
|
||||
}
|
||||
|
||||
boolean enforceMinRequiredBalance = false;
|
||||
if (command.parameterExists(enforceMinRequiredBalanceParamName)) {
|
||||
|
||||
+2
@@ -421,6 +421,8 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
|
||||
if (openingBalance.isGreaterThanZero()) {
|
||||
endOfDayBalance = openingBalance.minus(getAmount(currency));
|
||||
} else {
|
||||
endOfDayBalance = Money.of(currency, this.runningBalance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user