resoving merge conflicts with MIFOSX-1182

This commit is contained in:
Vishwas Babu A J
2014-05-09 19:26:48 +05:30
31 changed files with 603 additions and 184 deletions
+1 -1
View File
@@ -19297,7 +19297,7 @@ No Request Body:
<br/>
<table class=matrixHeading>
<tr class="matrixHeadingBG"><td><div class="mifosXHeading2">Optional Fields</div></td></tr>
<tr class=alt><td>accountNo, externalId, fieldOfficerId,linkAccountId</td></tr>
<tr class=alt><td>accountNo, externalId, fieldOfficerId,linkAccountId(if provided initial deposit amount will be collected from this account),transferInterestToSavings(By enabling this flag all interest postings will be transferred to linked saving account )</td></tr>
</table>
<br/>
<table class=matrixHeading>
@@ -0,0 +1,9 @@
ALTER TABLE `job_run_history`
CHANGE COLUMN `error_message` `error_message` TEXT NULL DEFAULT NULL AFTER `status`;
ALTER TABLE `m_deposit_account_term_and_preclosure`
ADD COLUMN `transfer_interest_to_linked_account` TINYINT(1) NOT NULL DEFAULT '0' AFTER `expected_firstdepositon_date`;
UPDATE `job` SET `scheduler_group`=1 WHERE `name`='Post Interest For Savings';
INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`, `is_misfired`) VALUES ('Transfer Interest To Savings', 'Transfer Interest To Savings', '0 2 0 1/1 * ? *', now(), 4, NULL, NULL, NULL, NULL, NULL, 1, 0, 1, 1, 0);
@@ -1,6 +1,9 @@
package org.mifosplatform.integrationtests;
import static org.junit.Assert.assertEquals;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
@@ -22,6 +25,9 @@ import org.mifosplatform.integrationtests.common.accounting.GLAccountBuilder;
import org.mifosplatform.integrationtests.common.accounting.JournalEntry;
import org.mifosplatform.integrationtests.common.accounting.JournalEntryHelper;
import org.mifosplatform.integrationtests.common.charges.ChargesHelper;
import org.mifosplatform.integrationtests.common.fixeddeposit.FixedDepositAccountHelper;
import org.mifosplatform.integrationtests.common.fixeddeposit.FixedDepositAccountStatusChecker;
import org.mifosplatform.integrationtests.common.fixeddeposit.FixedDepositProductHelper;
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker;
@@ -669,7 +675,7 @@ public class SchedulerJobsTestResults {
Assert.assertEquals("Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job",
chargeAmount, (Float) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"));
loanStatusHashMap = this.loanTransactionHelper.undoDisbursal(loanID);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
@@ -708,6 +714,88 @@ public class SchedulerJobsTestResults {
Assert.assertTrue(isNPAAfter);
}
@Test
public void testInterestTransferForSavings() throws InterruptedException {
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
this.schedulerJobHelper = new SchedulerJobHelper(this.requestSpec, this.responseSpec);
FixedDepositProductHelper fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec);
AccountHelper accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
FixedDepositAccountHelper fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec);
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM");
Calendar todaysDate = Calendar.getInstance();
todaysDate.add(Calendar.MONTH, -3);
final String VALID_FROM = dateFormat.format(todaysDate.getTime());
todaysDate.add(Calendar.YEAR, 10);
final String VALID_TO = dateFormat.format(todaysDate.getTime());
todaysDate = Calendar.getInstance();
todaysDate.add(Calendar.MONTH, -2);
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime());
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime());
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime());
todaysDate.add(Calendar.MONTH, 1);
final String WHOLE_TERM = "1";
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
Assert.assertNotNull(clientId);
Float balance = new Float(MINIMUM_OPENING_BALANCE) + new Float(FixedDepositAccountHelper.depositAmount);
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, String.valueOf(balance));
Assert.assertNotNull(savingsProductID);
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientId, savingsProductID,
ClientSavingsIntegrationTest.ACCOUNT_TYPE_INDIVIDUAL);
Assert.assertNotNull(savingsId);
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
HashMap summary = savingsAccountHelper.getSavingsSummary(savingsId);
assertEquals("Verifying opening Balance", balance, summary.get("accountBalance"));
Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO);
Assert.assertNotNull(fixedDepositProductId);
Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), VALID_FROM,
VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM, savingsId.toString(), true, fixedDepositAccountHelper);
Assert.assertNotNull(fixedDepositAccountId);
HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
this.responseSpec, fixedDepositAccountId.toString());
FixedDepositAccountStatusChecker.verifyFixedDepositIsPending(fixedDepositAccountStatusHashMap);
fixedDepositAccountStatusHashMap = fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId, APPROVED_ON_DATE);
FixedDepositAccountStatusChecker.verifyFixedDepositIsApproved(fixedDepositAccountStatusHashMap);
fixedDepositAccountStatusHashMap = fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE);
FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap);
summary = savingsAccountHelper.getSavingsSummary(savingsId);
balance = new Float(MINIMUM_OPENING_BALANCE);
assertEquals("Verifying Balance", balance, summary.get("accountBalance"));
fixedDepositAccountHelper.postInterestForFixedDeposit(fixedDepositAccountId);
HashMap fixedDepositSummary = savingsAccountHelper.getSavingsSummary(fixedDepositAccountId);
Float interestPosted = (Float) fixedDepositSummary.get("accountBalance") - new Float(FixedDepositAccountHelper.depositAmount);
String JobName = "Transfer Interest To Savings";
excuteJob(JobName);
fixedDepositSummary = savingsAccountHelper.getSavingsSummary(fixedDepositAccountId);
assertEquals("Verifying opening Balance", new Float(FixedDepositAccountHelper.depositAmount),
fixedDepositSummary.get("accountBalance"));
summary = savingsAccountHelper.getSavingsSummary(savingsId);
balance = new Float(MINIMUM_OPENING_BALANCE) + interestPosted;
validateNumberForEqualExcludePrecission(String.valueOf(balance), String.valueOf(summary.get("accountBalance")));
}
private void excuteJob(String JobName) throws InterruptedException {
ArrayList<HashMap> allSchedulerJobsData = this.schedulerJobHelper.getAllSchedulerJobs(this.requestSpec, this.responseSpec);
Assert.assertNotNull(allSchedulerJobsData);
@@ -797,4 +885,30 @@ public class SchedulerJobsTestResults {
.build(clientID, loanProductID, savingsID);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
private Integer createFixedDepositProduct(final String validFrom, final String validTo, Account... accounts) {
System.out.println("------------------------------CREATING NEW FIXED DEPOSIT PRODUCT ---------------------------------------");
FixedDepositProductHelper fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec);
final String fixedDepositProductJSON = fixedDepositProductHelper //
// .withAccountingRuleAsCashBased(accounts)
.build(validFrom, validTo);
return FixedDepositProductHelper.createFixedDepositProduct(fixedDepositProductJSON, requestSpec, responseSpec);
}
private Integer applyForFixedDepositApplication(final String clientID, final String productID, final String validFrom,
final String validTo, final String submittedOnDate, final String penalInterestType, String savingsId,
final boolean transferInterest, final FixedDepositAccountHelper fixedDepositAccountHelper) {
System.out.println("--------------------------------APPLYING FOR FIXED DEPOSIT ACCOUNT --------------------------------");
final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec)
//
.withSubmittedOnDate(submittedOnDate).withSavings(savingsId).transferInterest(true)
.withLockinPeriodFrequency("1", FixedDepositAccountHelper.DAYS)
.build(clientID, productID, validFrom, validTo, penalInterestType);
return fixedDepositAccountHelper.applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec);
}
public void validateNumberForEqualExcludePrecission(String val, String val2) {
DecimalFormat twoDForm = new DecimalFormat("#");
Assert.assertTrue(new Float(twoDForm.format(new Float(val))).compareTo(new Float(twoDForm.format(new Float(val2)))) == 0);
}
}
@@ -1,21 +1,12 @@
package org.mifosplatform.integrationtests.common.fixeddeposit;
import static org.junit.Assert.assertEquals;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mifosplatform.integrationtests.common.CommonConstants;
import org.mifosplatform.integrationtests.common.Utils;
import org.mifosplatform.integrationtests.common.accounting.Account;
import org.mifosplatform.integrationtests.common.accounting.Account.AccountType;
import org.mifosplatform.integrationtests.common.savings.SavingsApplicationTestBuilder;
import com.google.gson.Gson;
import com.jayway.restassured.specification.RequestSpecification;
@@ -49,7 +40,7 @@ public class FixedDepositAccountHelper {
private static final String DIGITS_AFTER_DECIMAL = "4";
private static final String IN_MULTIPLES_OF = "100";
private static final String USD = "USD";
private static final String DAYS = "0";
public static final String DAYS = "0";
private static final String WEEKS = "1";
private static final String MONTHS = "2";
private static final String YEARS = "3";
@@ -68,13 +59,13 @@ public class FixedDepositAccountHelper {
private String interestCalculationType = INTEREST_CALCULATION_USING_DAILY_BALANCE;
private String lockinPeriodFrequency = "6";
private String lockingPeriodFrequencyType = MONTHS;
private String minDepositTerm = "6";
private String minDepositTermTypeId = MONTHS;
private String maxDepositTerm = "10";
private String maxDepositTermTypeId = YEARS;
private String inMultiplesOfDepositTerm = "2";
private String inMultiplesOfDepositTermTypeId = MONTHS;
private String preClosurePenalInterest = "2";
private final String minDepositTerm = "6";
private final String minDepositTermTypeId = MONTHS;
private final String maxDepositTerm = "10";
private final String maxDepositTermTypeId = YEARS;
private final String inMultiplesOfDepositTerm = "2";
private final String inMultiplesOfDepositTermTypeId = MONTHS;
private final String preClosurePenalInterest = "2";
private String interestCalculationDaysInYearType = DAYS_365;
private final boolean preClosurePenalApplicable = true;
private final boolean isActiveChart = true;
@@ -83,6 +74,8 @@ public class FixedDepositAccountHelper {
private final String depositPeriod = "14";
private final String depositPeriodFrequencyId = MONTHS;
private String submittedOnDate = "";
private String savingsId = null;
private boolean transferInterest = false;
public String build(final String clientId, final String productId, final String validFrom, final String validTo,
final String penalInterestType) {
@@ -161,6 +154,8 @@ public class FixedDepositAccountHelper {
map.put("depositPeriod", this.depositPeriod);
map.put("depositPeriodFrequencyId", this.depositPeriodFrequencyId);
map.put("submittedOnDate", this.submittedOnDate);
map.put("linkAccountId", savingsId);
map.put("transferInterestToSavings", transferInterest);
String fixedDepositAccountJson = new Gson().toJson(map);
System.out.println(fixedDepositAccountJson);
@@ -201,9 +196,10 @@ public class FixedDepositAccountHelper {
return annualInterestRate;
}
public static Float getPrincipalAfterCompoundingInterest(Calendar currentDate, Float principal, Integer depositPeriod, double interestPerDay, Integer compoundingInterval, Integer postingInterval) {
public static Float getPrincipalAfterCompoundingInterest(Calendar currentDate, Float principal, Integer depositPeriod,
double interestPerDay, Integer compoundingInterval, Integer postingInterval) {
Float totalInterest = 0.0f;
Float interestEarned = 0.0f;
@@ -242,9 +238,10 @@ public class FixedDepositAccountHelper {
+ Utils.TENANT_IDENTIFIER, fixedDepositApplicationJSON, CommonConstants.RESPONSE_CHANGES);
}
public HashMap updateInterestCalculationConfigForFixedDeposit(final String clientID, final String productID, final String accountID, final String submittedOnDate,
final String validFrom, final String validTo, final String numberOfDaysPerYear, final String penalInterestType,
final String interestCalculationType, final String interestCompoundingPeriodType, final String interestPostingPeriodType) {
public HashMap updateInterestCalculationConfigForFixedDeposit(final String clientID, final String productID, final String accountID,
final String submittedOnDate, final String validFrom, final String validTo, final String numberOfDaysPerYear,
final String penalInterestType, final String interestCalculationType, final String interestCompoundingPeriodType,
final String interestPostingPeriodType) {
final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec) //
.withSubmittedOnDate(submittedOnDate) //
@@ -456,4 +453,19 @@ public class FixedDepositAccountHelper {
return this;
}
public FixedDepositAccountHelper withSavings(final String savingsId) {
this.savingsId = savingsId;
return this;
}
public FixedDepositAccountHelper transferInterest(final boolean transferInterest) {
this.transferInterest = transferInterest;
return this;
}
public FixedDepositAccountHelper withLockinPeriodFrequency(final String lockingPeriodFrequencyType, final String lockinPeriodFrequency) {
this.lockingPeriodFrequencyType = lockingPeriodFrequencyType;
this.lockinPeriodFrequency = lockinPeriodFrequency;
return this;
}
}
@@ -15,7 +15,8 @@ public enum JobName {
EXECUTE_STANDING_INSTRUCTIONS("Execute Standing Instruction"),
ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"),
UPDATE_NPA("Update Non Performing Assets"),
UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details");
UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"),
TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings");
private final String name;
@@ -13,7 +13,8 @@ public enum AccountTransferType {
INVALID(0, "accountTransferType.invalid"), //
ACCOUNT_TRANSFER(1, "accountTransferType.account.transfer"), //
LOAN_REPAYMENT(2, "accountTransferType.loan.repayment"), //
CHARGE_PAYMENT(3, "accountTransferType.charge.payment"); //
CHARGE_PAYMENT(3, "accountTransferType.charge.payment"), //
INTEREST_TRANSFER(4, "accountTransferType.interest.transfer"); //
private final Integer value;
private final String code;
@@ -31,6 +32,9 @@ public enum AccountTransferType {
case 3:
enumeration = AccountTransferType.CHARGE_PAYMENT;
break;
case 4:
enumeration = AccountTransferType.INTEREST_TRANSFER;
break;
}
return enumeration;
}
@@ -63,4 +67,8 @@ public enum AccountTransferType {
public boolean isChargePayment() {
return this.value.equals(AccountTransferType.CHARGE_PAYMENT.getValue());
}
public boolean isInterestTransfer() {
return this.value.equals(AccountTransferType.INTEREST_TRANSFER.getValue());
}
}
@@ -5,6 +5,8 @@
*/
package org.mifosplatform.portfolio.account.service;
import java.util.Collection;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
@@ -22,4 +24,6 @@ public interface AccountTransfersReadPlatformService {
boolean isAccountTransfer(Long transactionId, PortfolioAccountType accountType);
Page<AccountTransferData> retrieveByStandingInstruction(Long id, SearchParameters searchParameters);
Collection<Long> fetchPostInterestTransactionIds(Long accountId);
}
@@ -11,6 +11,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
@@ -25,6 +26,7 @@ import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
import org.mifosplatform.portfolio.account.domain.AccountTransferType;
import org.mifosplatform.portfolio.account.exception.AccountTransferNotFoundException;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
@@ -188,15 +190,15 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
}
@Override
public Page<AccountTransferData> retrieveAll(final SearchParameters searchParameters,final Long accountDetailId) {
public Page<AccountTransferData> retrieveAll(final SearchParameters searchParameters, final Long accountDetailId) {
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
sqlBuilder.append(this.accountTransfersMapper.schema());
Object[] finalObjectArray = {};
if(accountDetailId != null){
if (accountDetailId != null) {
sqlBuilder.append(" where att.account_transfer_details_id=?");
finalObjectArray = new Object[]{ accountDetailId };
finalObjectArray = new Object[] { accountDetailId };
}
if (searchParameters.isOrderByRequested()) {
@@ -231,6 +233,16 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
}
}
@Override
public Collection<Long> fetchPostInterestTransactionIds(final Long accountId) {
final String sql = "select att.from_savings_transaction_id from m_account_transfer_transaction att inner join m_account_transfer_details atd on atd.id = att.account_transfer_details_id where atd.from_savings_account_id=? and att.is_reversed =0 and atd.transfer_type = ?";
final List<Long> transactionId = this.jdbcTemplate.queryForList(sql, Long.class, accountId,
AccountTransferType.INTEREST_TRANSFER.getValue());
return transactionId;
}
private static final class AccountTransfersMapper implements RowMapper<AccountTransferData> {
private final String schemaSql;
@@ -98,13 +98,15 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
final PaymentDetail paymentDetail = null;
Long fromSavingsAccountId = null;
Long transferDetailId = null;
boolean isInterestTransfer = false;
if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) {
fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId);
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt,
transactionDate, transactionAmount, paymentDetail, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer());
transactionDate, transactionAmount, paymentDetail, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(),
isInterestTransfer);
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId);
@@ -123,7 +125,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId);
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt,
transactionDate, transactionAmount, paymentDetail, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer());
transactionDate, transactionAmount, paymentDetail, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(),
isInterestTransfer);
final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId);
@@ -244,7 +247,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount,
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
accountTransferDTO.getPaymentDetail(), fromSavingsAccount.isWithdrawalFeeApplicableForTransfer());
accountTransferDTO.getPaymentDetail(), fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), AccountTransferType
.fromInt(accountTransferDTO.getTransferType()).isInterestTransfer());
LoanTransaction loanTransaction = null;
@@ -284,7 +288,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
}
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount,
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
accountTransferDTO.getPaymentDetail(), fromSavingsAccount.isWithdrawalFeeApplicableForTransfer());
accountTransferDTO.getPaymentDetail(), fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), AccountTransferType
.fromInt(accountTransferDTO.getTransferType()).isInterestTransfer());
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount,
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
@@ -179,6 +179,7 @@ public class DepositsApiConstants {
public static final String preMatureCloseOnDateParamName = "preMatureCloseOnDate";
public static final String linkedAccountParamName = "linkAccountId";
public static final String transferInterestToSavingsParamName = "transferInterestToSavings";
// template
public static final String chartTemplate = "chartTemplate";
@@ -216,13 +217,11 @@ public class DepositsApiConstants {
private static final Set<String> DEPOSIT_TERM_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(minDepositTermParamName,
maxDepositTermParamName, minDepositTermTypeIdParamName, maxDepositTermTypeIdParamName, inMultiplesOfDepositTermParamName,
inMultiplesOfDepositTermTypeIdParamName, depositAmountParamName,
depositMinAmountParamName, depositMaxAmountParamName));
inMultiplesOfDepositTermTypeIdParamName, depositAmountParamName, depositMinAmountParamName, depositMaxAmountParamName));
private static final Set<String> DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(minDepositTermParamName,
maxDepositTermParamName, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTermParamName,
inMultiplesOfDepositTermType, depositAmountParamName,
depositMinAmountParamName, depositMaxAmountParamName));
inMultiplesOfDepositTermType, depositAmountParamName, depositMinAmountParamName, depositMaxAmountParamName));
private static final Set<String> RECURRING_DETAILS_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
recurringDepositAmountParamName, recurringDepositTypeIdParamName, recurringDepositFrequencyTypeIdParamName,
@@ -298,6 +297,7 @@ public class DepositsApiConstants {
fixedDepositRequestData.addAll(PRECLOSURE_REQUEST_DATA_PARAMETERS);
fixedDepositRequestData.addAll(DEPOSIT_TERM_REQUEST_DATA_PARAMETERS);
fixedDepositRequestData.add(linkedAccountParamName);
fixedDepositRequestData.add(transferInterestToSavingsParamName);
return fixedDepositRequestData;
}
@@ -307,6 +307,7 @@ public class DepositsApiConstants {
fixedDepositResponseData.addAll(PRECLOSURE_RESPONSE_DATA_PARAMETERS);
fixedDepositResponseData.addAll(DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS);
fixedDepositResponseData.add(linkedAccountParamName);
fixedDepositResponseData.add(transferInterestToSavingsParamName);
return fixedDepositResponseData;
}
@@ -45,6 +45,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@@ -279,8 +280,15 @@ public class DepositAccountDataValidator {
}
}
if (this.fromApiJsonHelper.parameterExists(linkedAccountParamName, element)) {
final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkedAccountParamName, element);
boolean isLinkedAccRequired = false;
if (fromApiJsonHelper.parameterExists(transferInterestToSavingsParamName, element)) {
isLinkedAccRequired = fromApiJsonHelper.extractBooleanNamed(transferInterestToSavingsParamName, element);
}
final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkedAccountParamName, element);
if (isLinkedAccRequired) {
baseDataValidator.reset().parameter(linkedAccountParamName).value(linkAccountId).notNull().longGreaterThanZero();
} else {
baseDataValidator.reset().parameter(linkedAccountParamName).value(linkAccountId).ignoreIfNull().longGreaterThanZero();
}
}
@@ -400,9 +408,18 @@ public class DepositAccountDataValidator {
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
}
boolean isLinkedAccRequired = false;
if (fromApiJsonHelper.parameterExists(transferInterestToSavingsParamName, element)) {
isLinkedAccRequired = fromApiJsonHelper.extractBooleanNamed(transferInterestToSavingsParamName, element);
}
if (this.fromApiJsonHelper.parameterExists(linkedAccountParamName, element)) {
final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkedAccountParamName, element);
baseDataValidator.reset().parameter(linkedAccountParamName).value(linkAccountId).ignoreIfNull().longGreaterThanZero();
if (isLinkedAccRequired) {
baseDataValidator.reset().parameter(linkedAccountParamName).value(linkAccountId).notNull().longGreaterThanZero();
} else {
baseDataValidator.reset().parameter(linkedAccountParamName).value(linkAccountId).ignoreIfNull().longGreaterThanZero();
}
}
}
@@ -585,6 +602,15 @@ public class DepositAccountDataValidator {
"Validation errors exist.", dataValidationErrors); }
}
public void throwLinkedAccountRequiredError() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final ApiParameterError error = ApiParameterError.parameterError(
"validation.msg.fixeddepositaccount.linkAccountId.cannot.be.blank", "Linked Savings account required", "linkAccountId");
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
private void validateSavingsCharges(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
if (element.isJsonObject()) {
@@ -49,6 +49,7 @@ public class FixedDepositAccountData extends DepositAccountData {
private EnumOptionData onAccountClosure;
private final PortfolioAccountData linkedAccount;
private final Boolean transferInterestToSavings;
private Collection<EnumOptionData> interestFreePeriodTypeOptions;
private Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions;
@@ -68,7 +69,7 @@ public class FixedDepositAccountData extends DepositAccountData {
final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, final BigDecimal maturityAmount,
final LocalDate maturityDate, final Integer depositPeriod, final EnumOptionData depositPeriodFrequency,
final EnumOptionData onAccountClosure) {
final EnumOptionData onAccountClosure, final Boolean transferInterestToSavings) {
final PortfolioAccountData linkedAccount = null;
final Collection<EnumOptionData> interestFreePeriodTypeOptions = null;
@@ -101,7 +102,7 @@ public class FixedDepositAccountData extends DepositAccountData {
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate,
depositPeriod, depositPeriodFrequency, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions,
depositPeriodFrequencyOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions,
savingsAccountDatas, linkedAccount);
savingsAccountDatas, linkedAccount, transferInterestToSavings);
}
public static FixedDepositAccountData withInterestChart(final FixedDepositAccountData account,
@@ -123,7 +124,8 @@ public class FixedDepositAccountData extends DepositAccountData {
account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount,
account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.depositTermTypeOptions,
account.inMultiplesOfDepositTermTypeOptions, account.depositTermTypeOptions, account.depositType, account.onAccountClosure,
account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts, account.linkedAccount);
account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts, account.linkedAccount,
account.transferInterestToSavings);
}
public static FixedDepositAccountData associationsAndTemplate(final FixedDepositAccountData account, FixedDepositAccountData template,
@@ -152,7 +154,7 @@ public class FixedDepositAccountData extends DepositAccountData {
account.maturityAmount, account.maturityDate, account.depositPeriod, account.depositPeriodFrequency,
template.depositTermTypeOptions, template.inMultiplesOfDepositTermTypeOptions, template.depositPeriodFrequencyOptions,
account.depositType, account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions,
template.savingsAccounts, linkedAccount);
template.savingsAccounts, linkedAccount, account.transferInterestToSavings);
}
public static FixedDepositAccountData withTemplateOptions(final FixedDepositAccountData account,
@@ -184,7 +186,8 @@ public class FixedDepositAccountData extends DepositAccountData {
account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount,
account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, depositTermTypeOptions,
inMultiplesOfDepositTermTypeOptions, depositPeriodFrequencyOptions, account.depositType, account.onAccountClosure,
account.onAccountClosureOptions, account.paymentTypeOptions, savingsAccounts, account.linkedAccount);
account.onAccountClosureOptions, account.paymentTypeOptions, savingsAccounts, account.linkedAccount,
account.transferInterestToSavings);
}
public static FixedDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -246,6 +249,7 @@ public class FixedDepositAccountData extends DepositAccountData {
final EnumOptionData depositPeriodFrequency = null;
final EnumOptionData onAccountClosure = null;
final PortfolioAccountData linkedAccount = null;
final Boolean transferInterestToSavings = null;
final Collection<EnumOptionData> interestFreePeriodTypeOptions = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
@@ -269,7 +273,8 @@ public class FixedDepositAccountData extends DepositAccountData {
preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
depositPeriodFrequency, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, depositPeriodFrequencyOptions,
depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount);
depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount,
transferInterestToSavings);
}
public static FixedDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount,
@@ -333,6 +338,7 @@ public class FixedDepositAccountData extends DepositAccountData {
final Integer depositPeriod = null;
final EnumOptionData depositPeriodFrequency = null;
final EnumOptionData onAccountClosure = null;
final Boolean transferInterestToSavings = null;
final Collection<EnumOptionData> interestFreePeriodTypeOptions = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
@@ -354,7 +360,8 @@ public class FixedDepositAccountData extends DepositAccountData {
preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
depositPeriodFrequency, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, depositPeriodFrequencyOptions,
depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount);
depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount,
transferInterestToSavings);
}
public static FixedDepositAccountData withClosureTemplateDetails(final FixedDepositAccountData account,
@@ -378,7 +385,8 @@ public class FixedDepositAccountData extends DepositAccountData {
account.inMultiplesOfDepositTerm, account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount,
account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.depositTermTypeOptions,
account.inMultiplesOfDepositTermTypeOptions, account.depositPeriodFrequencyOptions, account.depositType,
account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, account.linkedAccount);
account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, account.linkedAccount,
account.transferInterestToSavings);
}
private FixedDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId,
@@ -411,7 +419,7 @@ public class FixedDepositAccountData extends DepositAccountData {
final Collection<EnumOptionData> depositPeriodFrequencyOptions, final EnumOptionData depositType,
final EnumOptionData onAccountClosure, final Collection<EnumOptionData> onAccountClosureOptions,
final Collection<CodeValueData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas,
final PortfolioAccountData linkedAccount) {
final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings) {
super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId,
fieldofficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
@@ -442,6 +450,7 @@ public class FixedDepositAccountData extends DepositAccountData {
this.depositPeriodFrequency = depositPeriodFrequency;
this.onAccountClosure = onAccountClosure;
this.linkedAccount = linkedAccount;
this.transferInterestToSavings = transferInterestToSavings;
// template
this.interestFreePeriodTypeOptions = interestFreePeriodTypeOptions;
@@ -9,11 +9,12 @@ import static org.mifosplatform.portfolio.savings.DepositsApiConstants.chartIdPa
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositAmountParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodFrequencyIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositAmountParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName;
@@ -40,6 +41,7 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
@@ -71,7 +73,7 @@ import com.google.gson.JsonElement;
public class DepositAccountAssembler {
private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper;
private final SavingsHelper savingsHelper = new SavingsHelper();
private final SavingsHelper savingsHelper;
private final ClientRepositoryWrapper clientRepository;
private final GroupRepositoryWrapper groupRepository;
private final StaffRepositoryWrapper staffRepository;
@@ -88,7 +90,9 @@ public class DepositAccountAssembler {
final StaffRepositoryWrapper staffRepository, final FixedDepositProductRepository fixedDepositProductRepository,
final SavingsAccountRepositoryWrapper savingsAccountRepository,
final SavingsAccountChargeAssembler savingsAccountChargeAssembler, final FromJsonHelper fromApiJsonHelper,
final DepositProductAssembler depositProductAssembler, final RecurringDepositProductRepository recurringDepositProductRepository) {
final DepositProductAssembler depositProductAssembler,
final RecurringDepositProductRepository recurringDepositProductRepository,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper;
this.clientRepository = clientRepository;
this.groupRepository = groupRepository;
@@ -99,6 +103,7 @@ public class DepositAccountAssembler {
this.fromApiJsonHelper = fromApiJsonHelper;
this.depositProductAssembler = depositProductAssembler;
this.recurringDepositProductRepository = recurringDepositProductRepository;
this.savingsHelper = new SavingsHelper(accountTransfersReadPlatformService);
}
/**
@@ -314,6 +319,7 @@ public class DepositAccountAssembler {
final SavingsPeriodFrequencyType depositPeriodFrequency = SavingsPeriodFrequencyType.fromInt(depositPeriodFrequencyId);
final SavingsAccount account = null;
final LocalDate expectedFirstDepositOnDate = command.localDateValueOfParameterNamed(expectedFirstDepositOnDateParamName);
final Boolean trasferInterest = command.booleanPrimitiveValueOfParameterNamed(transferInterestToSavingsParamName);
// calculate maturity amount
final BigDecimal maturityAmount = null;// calculated and updated in
@@ -321,7 +327,8 @@ public class DepositAccountAssembler {
final LocalDate maturityDate = null;// calculated and updated in account
final DepositAccountOnClosureType accountOnClosureType = null;
return DepositAccountTermAndPreClosure.createNew(updatedProductPreClosure, updatedProductTerm, account, depositAmount,
maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType);
maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType,
trasferInterest);
}
public DepositAccountRecurringDetail assembleAccountRecurringDetail(final JsonCommand command,
@@ -82,14 +82,14 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, new Date());
final SavingsAccountTransaction withdrawal = account.withdraw(transactionDTO, applyWithdrawFee);
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount);
saveTransactionToGenerateTransactionId(withdrawal);
@@ -111,14 +111,14 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, new Date());
final SavingsAccountTransaction deposit = account.deposit(transactionDTO);
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
saveTransactionToGenerateTransactionId(deposit);
@@ -217,7 +217,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
}
this.savingsAccountRepository.save(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
return withdrawal;
@@ -9,6 +9,7 @@ import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositAm
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodFrequencyIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositPeriodParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
import java.math.BigDecimal;
import java.util.Date;
@@ -75,6 +76,9 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
@JoinColumn(name = "savings_account_id", nullable = false)
private SavingsAccount account;
@Column(name = "transfer_interest_to_linked_account", nullable = false)
private boolean transferInterestToLinkedAccount;
protected DepositAccountTermAndPreClosure() {
super();
}
@@ -82,16 +86,16 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
public static DepositAccountTermAndPreClosure createNew(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, final LocalDate expectedFirstDepositOnDate,
final DepositAccountOnClosureType accountOnClosureType) {
final DepositAccountOnClosureType accountOnClosureType, Boolean trasferInterest) {
return new DepositAccountTermAndPreClosure(preClosureDetail, depositTermDetail, account, depositAmount, maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType);
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType, trasferInterest);
}
private DepositAccountTermAndPreClosure(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, final LocalDate expectedFirstDepositOnDate,
final DepositAccountOnClosureType accountOnClosureType) {
final DepositAccountOnClosureType accountOnClosureType, Boolean transferInterest) {
this.depositAmount = depositAmount;
this.maturityAmount = maturityAmount;
this.maturityDate = (maturityDate == null) ? null : maturityDate.toDate();
@@ -102,6 +106,7 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
this.account = account;
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate == null ? null : expectedFirstDepositOnDate.toDate();
this.onAccountClosureType = (accountOnClosureType == null) ? null : accountOnClosureType.getValue();
this.transferInterestToLinkedAccount = transferInterest;
}
public Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
@@ -124,10 +129,10 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
actualChanges.put(depositPeriodFrequencyIdParamName, SavingsEnumerations.depositTermFrequencyType(newValue));
this.depositPeriodFrequency = newValue;
}
final String localeAsInput = command.locale();
final String dateFormat = command.dateFormat();
if (command.isChangeInLocalDateParameterNamed(expectedFirstDepositOnDateParamName, getExpectedFirstDepositOnDate())) {
final LocalDate newValue = command.localDateValueOfParameterNamed(expectedFirstDepositOnDateParamName);
final String newValueAsString = command.stringValueOfParameterNamed(expectedFirstDepositOnDateParamName);
@@ -137,6 +142,12 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
this.expectedFirstDepositOnDate = newValue.toDate();
}
if (command.isChangeInBooleanParameterNamed(transferInterestToSavingsParamName, this.transferInterestToLinkedAccount)) {
final Boolean newValue = command.booleanPrimitiveValueOfParameterNamed(transferInterestToSavingsParamName);
actualChanges.put(transferInterestToSavingsParamName, newValue);
this.transferInterestToLinkedAccount = newValue;
}
if (this.preClosureDetail != null) {
actualChanges.putAll(this.preClosureDetail.update(command, baseDataValidator));
}
@@ -262,13 +273,20 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
final DepositPreClosureDetail preClosureDetail = this.preClosureDetail.copy();
final DepositTermDetail depositTermDetail = this.depositTermDetail.copy();
final LocalDate expectedFirstDepositOnDate = null;
final Boolean transferInterestToLinkedAccount = null;
final DepositAccountOnClosureType accountOnClosureType = null;
return DepositAccountTermAndPreClosure.createNew(preClosureDetail, depositTermDetail, account, actualDepositAmount, maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType);
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType,
transferInterestToLinkedAccount);
}
public void updateExpectedFirstDepositDate(final LocalDate expectedFirstDepositOnDate) {
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate.toDate();
}
public boolean isTransferInterestToLinkedAccount() {
return this.transferInterestToLinkedAccount;
}
}
@@ -13,6 +13,7 @@ import static org.mifosplatform.portfolio.savings.DepositsApiConstants.onAccount
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@@ -259,11 +260,13 @@ public class FixedDepositAccount extends SavingsAccount {
final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType);
final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, maturityDate);
final Collection<Long> interestPostTransactions = this.savingsHelper.fetchPostInterestTransactionIds(getId());
boolean isInterestTransfer = false;
for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions,
this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
maturityDate);
maturityDate, interestPostTransactions, isInterestTransfer);
periodStartingBalance = postingPeriod.closingBalance();
@@ -271,7 +274,8 @@ public class FixedDepositAccount extends SavingsAccount {
}
this.summary.updateFromInterestPeriodSummaries(this.currency, allPostingPeriods);
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods);
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods, this.getLockedInUntilLocalDate(),
isTransferInterestToOtherAccount());
return allPostingPeriods;
}
@@ -429,8 +433,8 @@ public class FixedDepositAccount extends SavingsAccount {
public void postMaturityInterest(final LocalDate interestPostingUpToDate) {
final MathContext mc = MathContext.DECIMAL64;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate);
final boolean isInterestTransfer = false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
Money interestPostedToDate = Money.zero(this.currency);
@@ -526,15 +530,15 @@ public class FixedDepositAccount extends SavingsAccount {
}
@Override
public void postInterest(final MathContext mc, final LocalDate postingDate) {
public void postInterest(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
super.postInterest(mc, interestPostingUpToDate);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer);
}
@Override
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate) {
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
}
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
@@ -680,4 +684,9 @@ public class FixedDepositAccount extends SavingsAccount {
return reInvestedAccount;
}
@Override
protected boolean isTransferInterestToOtherAccount() {
return this.accountTermAndPreClosure.isTransferInterestToLinkedAccount();
}
}
@@ -13,6 +13,7 @@ import static org.mifosplatform.portfolio.savings.DepositsApiConstants.onAccount
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -281,18 +282,20 @@ public class RecurringDepositAccount extends SavingsAccount {
final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType);
final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, maturityDate);
final Collection<Long> interestPostTransactions = this.savingsHelper.fetchPostInterestTransactionIds(getId());
boolean isInterestTransfer = false;
for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, transactions,
this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
maturityDate);
maturityDate, interestPostTransactions, isInterestTransfer);
periodStartingBalance = postingPeriod.closingBalance();
allPostingPeriods.add(postingPeriod);
}
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods);
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods, this.getLockedInUntilLocalDate(),
isTransferInterestToOtherAccount());
// this.summary.updateFromInterestPeriodSummaries(this.currency,
// allPostingPeriods);
return allPostingPeriods;
@@ -538,8 +541,8 @@ public class RecurringDepositAccount extends SavingsAccount {
public void postMaturityInterest(final LocalDate interestPostingUpToDate) {
final MathContext mc = MathContext.DECIMAL64;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate);
boolean isInterestTransfer = false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate,isInterestTransfer);
Money interestPostedToDate = Money.zero(this.currency);
@@ -634,15 +637,15 @@ public class RecurringDepositAccount extends SavingsAccount {
}
@Override
public void postInterest(final MathContext mc, final LocalDate postingDate) {
public void postInterest(final MathContext mc, final LocalDate postingDate,final boolean isInterestTransfer) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
super.postInterest(mc, interestPostingUpToDate);
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer);
}
@Override
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate) {
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer) {
final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate);
return super.calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
}
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
@@ -891,4 +894,8 @@ public class RecurringDepositAccount extends SavingsAccount {
this.activatedOnDate = now.toDate();
}
@Override
protected boolean isTransferInterestToOtherAccount() {
return this.accountTermAndPreClosure.isTransferInterestToLinkedAccount();
}
}
@@ -370,9 +370,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return SavingsAccountStatusType.fromInt(this.status).isClosed();
}
public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate) {
public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer) {
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate);
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer);
Money interestPostedToDate = Money.zero(this.currency);
@@ -447,8 +447,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
* calculate the amount of interest due at the end of each 'crediting'
* period check if an existing 'interest posting' transaction exists for
* date and matches the amount posted
* @param isInterestTransfer TODO
*/
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate) {
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate, boolean isInterestTransfer) {
// no openingBalance concept supported yet but probably will to allow
// for migrations.
@@ -480,18 +481,20 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType);
final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, upToInterestCalculationDate);
final Collection<Long> interestPostTransactions = this.savingsHelper.fetchPostInterestTransactionIds(getId());
for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance,
retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType,
interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate);
interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate, interestPostTransactions, isInterestTransfer);
periodStartingBalance = postingPeriod.closingBalance();
allPostingPeriods.add(postingPeriod);
}
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods);
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods, getLockedInUntilLocalDate(),
isTransferInterestToOtherAccount());
this.summary.updateFromInterestPeriodSummaries(this.currency, allPostingPeriods);
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
@@ -809,7 +812,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return isLocked;
}
private LocalDate getLockedInUntilLocalDate() {
protected LocalDate getLockedInUntilLocalDate() {
LocalDate lockedInUntilLocalDate = null;
if (this.lockedInUntilDate != null) {
lockedInUntilLocalDate = new LocalDate(this.lockedInUntilDate);
@@ -2036,4 +2039,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
public DepositAccountType depositAccountType() {
return DepositAccountType.fromInt(depositType);
}
protected boolean isTransferInterestToOtherAccount() {
return false;
}
}
@@ -34,6 +34,7 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
@@ -59,7 +60,7 @@ import com.google.gson.JsonElement;
public class SavingsAccountAssembler {
private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper;
private final SavingsHelper savingsHelper = new SavingsHelper();
private final SavingsHelper savingsHelper;
private final ClientRepositoryWrapper clientRepository;
private final GroupRepositoryWrapper groupRepository;
private final StaffRepositoryWrapper staffRepository;
@@ -73,7 +74,8 @@ public class SavingsAccountAssembler {
final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository,
final StaffRepositoryWrapper staffRepository, final SavingsProductRepository savingProductRepository,
final SavingsAccountRepositoryWrapper savingsAccountRepository,
final SavingsAccountChargeAssembler savingsAccountChargeAssembler, final FromJsonHelper fromApiJsonHelper) {
final SavingsAccountChargeAssembler savingsAccountChargeAssembler, final FromJsonHelper fromApiJsonHelper,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper;
this.clientRepository = clientRepository;
this.groupRepository = groupRepository;
@@ -82,6 +84,7 @@ public class SavingsAccountAssembler {
this.savingsAccountRepository = savingsAccountRepository;
this.savingsAccountChargeAssembler = savingsAccountChargeAssembler;
this.fromApiJsonHelper = fromApiJsonHelper;
savingsHelper = new SavingsHelper(accountTransfersReadPlatformService);
}
/**
@@ -14,7 +14,7 @@ import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
public interface SavingsAccountDomainService {
SavingsAccountTransaction handleWithdrawal(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean applyWithdrawFee);
BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean applyWithdrawFee, boolean isInterestTransfer);
SavingsAccountTransaction handleDeposit(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail);
@@ -48,7 +48,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
@Override
public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final boolean applyWithdrawFee) {
final boolean applyWithdrawFee, boolean isInterestTransfer) {
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
@@ -60,10 +60,10 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount);
saveTransactionToGenerateTransactionId(withdrawal);
@@ -79,6 +79,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail) {
boolean isInterestTransfer = false;
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
@@ -89,10 +90,10 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
saveTransactionToGenerateTransactionId(deposit);
@@ -445,13 +445,15 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
if (isDeposit()) {
// endOfDayBalance = openingBalance.plus(getAmount(currency));
// if (endOfDayBalance.isLessThanZero()) {
endOfDayBalance = Money.of(currency, this.runningBalance);
endOfDayBalance = endOfDayBalance.plus(getAmount(currency));
// }
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
// endOfDayBalance = openingBalance.minus(getAmount(currency));
// if (endOfDayBalance.isLessThanZero()) {
endOfDayBalance = Money.of(currency, this.runningBalance);
// }
if (endOfDayBalance.isGreaterThanZero()) {
endOfDayBalance = endOfDayBalance.minus(getAmount(currency));
} else {
endOfDayBalance = Money.of(currency, this.runningBalance);
}
}
}
@@ -1,6 +1,7 @@
package org.mifosplatform.portfolio.savings.domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.joda.time.DateTime;
@@ -8,12 +9,19 @@ import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.interest.CompoundInterestHelper;
import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod;
public final class SavingsHelper {
AccountTransfersReadPlatformService accountTransfersReadPlatformService = null;
public SavingsHelper(AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
}
private final CompoundInterestHelper compoundInterestHelper = new CompoundInterestHelper();
public List<LocalDateInterval> determineInterestPostingPeriods(final LocalDate activationLocalDate,
@@ -26,7 +34,8 @@ public final class SavingsHelper {
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType, interestPostingUpToDate);
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType,
interestPostingUpToDate);
periodEndDate = interestPostingLocalDate.minusDays(1);
if (!interestPostingLocalDate.isAfter(interestPostingUpToDate)) {
@@ -61,7 +70,7 @@ public final class SavingsHelper {
final int monthofYear = periodStartDate.getMonthOfYear();
if (monthofYear <= 3) {
periodEndDate = new DateTime().withDate(year, 3, 31).toLocalDate();
} else if (monthofYear <= 6) {
} else if (monthofYear <= 6) {
periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate();
} else if (monthofYear <= 9) {
periodEndDate = new DateTime().withDate(year, 9, 30).toLocalDate();
@@ -70,14 +79,14 @@ public final class SavingsHelper {
}
break;
case BIANNUAL:
final int byear = periodStartDate.getYearOfEra();
final int bmonthofYear = periodStartDate.getMonthOfYear();
final int byear = periodStartDate.getYearOfEra();
final int bmonthofYear = periodStartDate.getMonthOfYear();
if (bmonthofYear <= 6) {
periodEndDate = new DateTime().withDate(byear, 6, 30).toLocalDate();
} else if (bmonthofYear <= 12) {
periodEndDate = new DateTime().withDate(byear, 12, 31).toLocalDate();
}
break;
break;
case ANNUAL:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
@@ -90,7 +99,13 @@ public final class SavingsHelper {
return periodEndDate;
}
public Money calculateInterestForAllPostingPeriods(final MonetaryCurrency currency, final List<PostingPeriod> allPeriods) {
return this.compoundInterestHelper.calculateInterestForAllPostingPeriods(currency, allPeriods);
public Money calculateInterestForAllPostingPeriods(final MonetaryCurrency currency, final List<PostingPeriod> allPeriods,
LocalDate accountLockedUntil, Boolean immediateWithdrawalOfInterest) {
return this.compoundInterestHelper.calculateInterestForAllPostingPeriods(currency, allPeriods, accountLockedUntil,
immediateWithdrawalOfInterest);
}
public Collection<Long> fetchPostInterestTransactionIds(Long accountId) {
return this.accountTransfersReadPlatformService.fetchPostInterestTransactionIds(accountId);
}
}
@@ -3,12 +3,26 @@ package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
public class CompoundInterestHelper {
public Money calculateInterestForAllPostingPeriods(final MonetaryCurrency currency, final List<PostingPeriod> allPeriods) {
/**
* @param currency
* @param allPeriods
* @param lockUntil
* - account locked date used with the combination of
* immediateWithdrawalOfInterest to avoid exclusion of
* interestEarned
* @param interestTransferEnabled
* - boolean flag used to avoid addition of interest to next
* posting period as income while calculating interest
* @return
*/
public Money calculateInterestForAllPostingPeriods(final MonetaryCurrency currency, final List<PostingPeriod> allPeriods,
LocalDate lockUntil, Boolean interestTransferEnabled) {
// sum up the 'rounded' values that are posted each posting period
Money interestEarned = Money.zero(currency);
@@ -22,8 +36,14 @@ public class CompoundInterestHelper {
final Money moneyToBePostedForPeriod = Money.of(currency, interestEarnedThisPeriod);
interestEarned = interestEarned.plus(moneyToBePostedForPeriod);
interestEarnedButNotPosted = interestEarnedButNotPosted.add(moneyToBePostedForPeriod.getAmount());
// these checks are for fixed deposit account for not include
// interest for accounts which has post interest to linked savings
// account and if already transfered then it includes in interest
// calculation.
if (postingPeriod.isInterestTransfered() || !interestTransferEnabled
|| (lockUntil != null && !postingPeriod.dateOfPostingTransaction().isAfter(lockUntil))) {
interestEarnedButNotPosted = interestEarnedButNotPosted.add(moneyToBePostedForPeriod.getAmount());
}
}
return interestEarned;
@@ -2,6 +2,7 @@ package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.joda.time.DateTime;
@@ -33,13 +34,18 @@ public class PostingPeriod {
private final Money closingBalance;
private final SavingsInterestCalculationType interestCalculationType;
// include in compounding interest
private boolean interestTransfered = false;
// isInterestTransfer boolean is to identify newly created transaction is interest transfer
public static PostingPeriod createFrom(final LocalDateInterval periodInterval, final Money periodStartingBalance,
final List<SavingsAccountTransaction> orderedListOfTransactions, final MonetaryCurrency currency,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear, final LocalDate upToInterestCalculationDate) {
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final LocalDate upToInterestCalculationDate, Collection<Long> interestPostTransactions, boolean isInterestTransfer) {
final List<EndOfDayBalance> accountEndOfDayBalances = new ArrayList<EndOfDayBalance>();
boolean interestTransfered = false;
Money openingDayBalance = periodStartingBalance;
Money closeOfDayBalance = openingDayBalance;
for (final SavingsAccountTransaction transaction : orderedListOfTransactions) {
@@ -51,6 +57,7 @@ public class PostingPeriod {
accountEndOfDayBalances.add(endOfDayBalance);
openingDayBalance = endOfDayBalance.closingBalance();
} else if (transaction.spansAnyPortionOf(periodInterval)) {
final EndOfDayBalance endOfDayBalance = transaction.toEndOfDayBalanceBoundedBy(openingDayBalance, periodInterval);
accountEndOfDayBalances.add(endOfDayBalance);
@@ -58,22 +65,32 @@ public class PostingPeriod {
closeOfDayBalance = endOfDayBalance.closingBalance();
openingDayBalance = closeOfDayBalance;
}
// this check is to make sure to add interest if withdrawal is happened for already
if (transaction.occursOn(periodInterval.endDate().plusDays(1))) {
if (transaction.getId() == null) {
interestTransfered = isInterestTransfer;
} else if (interestPostTransactions.contains(transaction.getId())) {
interestTransfered = true;
}
}
}
if (accountEndOfDayBalances.isEmpty()) {
LocalDate balanceStartDate = periodInterval.startDate();
LocalDate balanceEndDate = periodInterval.endDate();
Integer numberOfDaysOfBalance = periodInterval.daysInPeriodInclusiveOfEndDate();
if (balanceEndDate.isAfter(upToInterestCalculationDate)) {
balanceEndDate = upToInterestCalculationDate;
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
}
final EndOfDayBalance endOfDayBalance = EndOfDayBalance.from(balanceStartDate, openingDayBalance, closeOfDayBalance,
numberOfDaysOfBalance);
accountEndOfDayBalances.add(endOfDayBalance);
closeOfDayBalance = endOfDayBalance.closingBalance();
@@ -84,13 +101,13 @@ public class PostingPeriod {
accountEndOfDayBalances, upToInterestCalculationDate);
return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType,
interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods);
interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods, interestTransfered);
}
private PostingPeriod(final LocalDateInterval periodInterval, final MonetaryCurrency currency, final Money openingBalance,
final Money closingBalance, final SavingsCompoundingInterestPeriodType interestCompoundingType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final List<CompoundingPeriod> compoundingPeriods) {
final List<CompoundingPeriod> compoundingPeriods, boolean interestTransfered) {
this.periodInterval = periodInterval;
this.currency = currency;
this.openingBalance = openingBalance;
@@ -102,6 +119,7 @@ public class PostingPeriod {
this.compoundingPeriods = compoundingPeriods;
this.dateOfPostingTransaction = periodInterval.endDate().plusDays(1);
this.interestTransfered = interestTransfered;
}
public Money interest() {
@@ -115,7 +133,7 @@ public class PostingPeriod {
public Money closingBalance() {
return this.closingBalance;
}
public Money openingBalance() {
return this.openingBalance;
}
@@ -145,7 +163,8 @@ public class PostingPeriod {
}
private static List<CompoundingPeriod> compoundingPeriodsInPostingPeriod(final LocalDateInterval postingPeriodInterval,
final SavingsCompoundingInterestPeriodType interestPeriodType, final List<EndOfDayBalance> allEndOfDayBalances, final LocalDate upToInterestCalculationDate) {
final SavingsCompoundingInterestPeriodType interestPeriodType, final List<EndOfDayBalance> allEndOfDayBalances,
final LocalDate upToInterestCalculationDate) {
final List<CompoundingPeriod> compoundingPeriods = new ArrayList<CompoundingPeriod>();
@@ -174,7 +193,8 @@ public class PostingPeriod {
final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate);
if (postingPeriodInterval.contains(compoundingPeriodInterval)) {
compoundingPeriod = MonthlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances, upToInterestCalculationDate);
compoundingPeriod = MonthlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances,
upToInterestCalculationDate);
compoundingPeriods.add(compoundingPeriod);
}
@@ -182,11 +202,11 @@ public class PostingPeriod {
periodStartDate = periodEndDate.plusDays(1);
}
break;
// case WEEKLY:
// break;
// case BIWEEKLY:
// break;
case QUATERLY:
// case WEEKLY:
// break;
// case BIWEEKLY:
// break;
case QUATERLY:
final LocalDate qPostingPeriodEndDate = postingPeriodInterval.endDate();
periodStartDate = postingPeriodInterval.startDate();
@@ -210,7 +230,7 @@ public class PostingPeriod {
// move periodStartDate forward to day after this period
periodStartDate = periodEndDate.plusDays(1);
}
break;
break;
case BI_ANNUAL:
final LocalDate bPostingPeriodEndDate = postingPeriodInterval.endDate();
@@ -235,7 +255,7 @@ public class PostingPeriod {
// move periodStartDate forward to day after this period
periodStartDate = periodEndDate.plusDays(1);
}
break;
break;
case ANNUAL:
final LocalDate aPostingPeriodEndDate = postingPeriodInterval.endDate();
@@ -260,9 +280,9 @@ public class PostingPeriod {
// move periodStartDate forward to day after this period
periodStartDate = periodEndDate.plusDays(1);
}
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
}
return compoundingPeriods;
@@ -291,7 +311,7 @@ public class PostingPeriod {
// produce period end date on last day of current month
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
break;
case QUATERLY:
case QUATERLY:
// // jan 1st to mar 31st, 1st apr to jun 30, jul 1st to sept
// 30,
// // oct 1st to dec 31
@@ -306,7 +326,7 @@ public class PostingPeriod {
} else if (monthofYear <= 12) {
periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
}
break;
break;
case BI_ANNUAL:
// // jan 1st to 30, jul 1st to dec 31
year = periodStartDate.getYearOfEra();
@@ -316,18 +336,22 @@ public class PostingPeriod {
} else if (monthofYear <= 12) {
periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
}
break;
break;
case ANNUAL:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
// periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
// break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
// periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
// break;
}
return periodEndDate;
}
public boolean isInterestTransfered() {
return this.interestTransfered;
}
}
@@ -9,6 +9,7 @@ import java.util.Collection;
import org.mifosplatform.infrastructure.core.data.PaginationParameters;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.portfolio.account.data.AccountTransferDTO;
import org.mifosplatform.portfolio.savings.DepositAccountType;
import org.mifosplatform.portfolio.savings.data.DepositAccountData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData;
@@ -34,4 +35,6 @@ public interface DepositAccountReadPlatformService {
Collection<DepositAccountData> retrieveForMaturityUpdate();
SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(final Long accountId);
Collection<AccountTransferDTO> retrieveDataForInterestTransfer();
}
@@ -26,7 +26,10 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.data.AccountTransferDTO;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
import org.mifosplatform.portfolio.account.domain.AccountTransferType;
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientData;
@@ -464,6 +467,22 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
}
}
@Override
public Collection<AccountTransferDTO> retrieveDataForInterestTransfer() {
final StringBuilder sqlBuilder = new StringBuilder(300);
AccountTransferMapper mapper = new AccountTransferMapper();
sqlBuilder.append("SELECT ");
sqlBuilder.append(mapper.schema());
sqlBuilder.append(" where da.transfer_interest_to_linked_account = 1 and ");
sqlBuilder
.append("st.transaction_date > (select IFNULL(max(sat.transaction_date),sa.activatedon_date) from m_savings_account_transaction sat where sat.transaction_type_enum = ? and sat.savings_account_id = sa.id and sat.is_reversed=0) ");
sqlBuilder
.append("and st.transaction_type_enum = ? and sa.status_enum = ? and st.is_reversed=0 and st.transaction_date > IFNULL(sa.lockedin_until_date_derived,sa.activatedon_date)");
return this.jdbcTemplate.query(sqlBuilder.toString(), mapper, new Object[] { SavingsAccountTransactionType.WITHDRAWAL.getValue(),
SavingsAccountTransactionType.INTEREST_POSTING.getValue(), SavingsAccountStatusType.ACTIVE.getValue() });
}
private static abstract class DepositAccountMapper implements RowMapper<DepositAccountData> {
private final String selectFieldsSql;
@@ -697,7 +716,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
sqlBuilder.append("datp.maturity_date as maturityDate, ");
sqlBuilder.append("datp.deposit_period as depositPeriod, ");
sqlBuilder.append("datp.deposit_period_frequency_enum as depositPeriodFrequencyTypeId, ");
sqlBuilder.append("datp.on_account_closure_enum as onAccountClosureId ");
sqlBuilder.append("datp.on_account_closure_enum as onAccountClosureId, ");
sqlBuilder.append("datp.transfer_interest_to_linked_account as transferInterestToSavings ");
sqlBuilder.append(super.selectTablesSql());
@@ -750,12 +770,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
final Integer onAccountClosureId = JdbcSupport.getInteger(rs, "onAccountClosureId");
final EnumOptionData onAccountClosureType = (onAccountClosureId == null) ? null : SavingsEnumerations
.depositAccountOnClosureType(onAccountClosureId);
final Boolean transferInterestToSavings = rs.getBoolean("transferInterestToSavings");
return FixedDepositAccountData.instance(depositAccountData, interestFreePeriodApplicable, interestFreeFromPeriod,
interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
depositPeriodFrequencyType, onAccountClosureType);
depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings);
}
}
@@ -1182,12 +1203,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
final Integer depositPeriod = null;
final EnumOptionData depositPeriodFrequencyType = null;
final EnumOptionData onAccountClosureType = null;
final Boolean transferInterestToSavings = false;
return FixedDepositAccountData.instance(depositAccountData, interestFreePeriodApplicable, interestFreeFromPeriod,
interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
depositPeriodFrequencyType, onAccountClosureType);
depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings);
}
}
@@ -1375,4 +1397,36 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
DateUtils.getLocalDateOfTenant(), currency, recurringDepositAmount, runningBalance, false, transfer);
}
}
private class AccountTransferMapper implements RowMapper<AccountTransferDTO> {
private final String schemaSql;
public AccountTransferMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder
.append("sa.id as fromAcc ,aa.linked_savings_account_id as toAcc,st.amount as amount, st.transaction_date as transactionDate ")
.append(" from m_deposit_account_term_and_preclosure da ")
.append(" inner join m_savings_account sa on da.savings_account_id = sa.id")
.append(" inner join m_savings_account_transaction st on st.savings_account_id = sa.id")
.append(" inner join m_portfolio_account_associations aa on aa.savings_account_id=sa.id");
schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public AccountTransferDTO mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final Long fromAccountId = rs.getLong("fromAcc");
final Long toAccountId = rs.getLong("toAcc");
final BigDecimal transactionAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount");
final LocalDate transactionDate = JdbcSupport.getLocalDate(rs, "transactionDate");
return new AccountTransferDTO(transactionDate, transactionAmount, PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS,
fromAccountId, toAccountId, "trasfer interest to savings", null, null, null, null, null, null, null,
AccountTransferType.INTEREST_TRANSFER.getValue(), null, null, null, null, null);
}
}
}
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.savings.service;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.portfolio.savings.DepositAccountType;
@@ -54,4 +55,6 @@ public interface DepositAccountWritePlatformService {
void applyChargeDue(final Long savingsAccountChargeId, final Long accountId, final DepositAccountType depositAccountType);
void updateMaturityDetails(final Long depositAccountId, final DepositAccountType depositAccountType);
void transferInterestToSavings() throws JobExecutionException;
}
@@ -14,6 +14,7 @@ import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -34,6 +35,9 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.exception.PlatformServiceUnavailableException;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.holiday.service.HolidayWritePlatformService;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
@@ -78,6 +82,7 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository;
import org.mifosplatform.portfolio.savings.exception.DepositAccountTransactionNotAllowedException;
import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException;
import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
import org.mifosplatform.portfolio.savings.exception.TransactionUpdateNotAllowedException;
import org.mifosplatform.useradministration.domain.AppUser;
@@ -106,6 +111,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
private final WorkingDaysWritePlatformService workingDaysWritePlatformService;
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
private final DepositAccountReadPlatformService depositAccountReadPlatformService;
@Autowired
public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -123,7 +129,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final HolidayWritePlatformService holidayWritePlatformService,
final WorkingDaysWritePlatformService workingDaysWritePlatformService,
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService) {
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final DepositAccountReadPlatformService depositAccountReadPlatformService) {
this.context = context;
this.savingAccountRepository = savingAccountRepository;
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
@@ -142,6 +149,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.workingDaysWritePlatformService = workingDaysWritePlatformService;
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
this.depositAccountReadPlatformService = depositAccountReadPlatformService;
}
@Transactional
@@ -282,8 +290,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN);
boolean isInterestTransfer = false;
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
this.savingAccountRepository.save(account);
@@ -319,13 +328,33 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
account.postInterest(mc, today);
boolean isInterestTransfer = false;
account.postInterest(mc, today, isInterestTransfer);
this.savingAccountRepository.save(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
}
@Override
@CronTarget(jobName = JobName.TRANSFER_INTEREST_TO_SAVINGS)
public void transferInterestToSavings() throws JobExecutionException {
Collection<AccountTransferDTO> accountTrasferData = this.depositAccountReadPlatformService.retrieveDataForInterestTransfer();
StringBuilder sb = new StringBuilder(200);
for (AccountTransferDTO accountTransferDTO : accountTrasferData) {
try {
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
} catch (final PlatformApiDataValidationException e) {
sb.append("Validation exception while trasfering Interest form ").append(accountTransferDTO.getFromAccountId())
.append(" to ").append(accountTransferDTO.getToAccountId()).append("--------");
} catch (final InsufficientAccountBalanceException e) {
sb.append("InsufficientAccountBalance Exception while trasfering Interest form ")
.append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId())
.append("--------");
}
}
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
}
@Override
public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId,
final boolean allowAccountTransferModification, final DepositAccountType depositAccountType) {
@@ -354,13 +383,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
throwValidationForActiveStatus(SavingsApiConstants.undoTransactionAction);
}
account.undoTransaction(transactionId);
boolean isInterestTransfer = false;
checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction);
account.activateAccountBasedOnBalance();
@@ -425,12 +454,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
transaction = account.withdraw(transactionDTO, true);
}
final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction);
boolean isInterestTransfer = false;
if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction);
account.activateAccountBasedOnBalance();
@@ -548,7 +577,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
savingsAccount.getTransactions().add(newTransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate);
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -572,7 +602,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
savingsAccount.getTransactions().add(withdrawtransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate);
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -605,9 +636,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
if (fieldOfficer != null) {
savingsAccount.update(fieldOfficer);
}
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate);
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -745,14 +776,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.waiveCharge(savingsAccountChargeId);
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction);
@@ -863,14 +894,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter);
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode());
@@ -5,6 +5,9 @@
*/
package org.mifosplatform.portfolio.savings.service;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.linkedAccountParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -272,13 +275,15 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
if (depositAccountType.isFixedDeposit()) {
((FixedDepositAccount) account).updateMaturityDateAndAmountBeforeAccountActivation(mc);
} else if (depositAccountType.isRecurringDeposit()) {
final LocalDate transactionStartDate = ((RecurringDepositAccount) account).depositStartDate();
final LocalDate transactionStartDate = ((RecurringDepositAccount) account).depositStartDate();
((RecurringDepositAccount) account).updateMaturityDateAndAmount(mc, transactionStartDate);
}
this.savingAccountRepository.saveAndFlush(account);
}
boolean isLinkedAccRequired = command.booleanPrimitiveValueOfParameterNamed(transferInterestToSavingsParamName);
// Save linked account information
final Long savingsAccountId = command.longValueOfParameterNamed(DepositsApiConstants.linkedAccountParamName);
AccountAssociations accountAssociations = this.accountAssociationsRepository.findBySavingsId(accountId);
@@ -287,7 +292,12 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
if (this.fromJsonHelper.parameterExists(DepositsApiConstants.linkedAccountParamName, command.parsedJson())) {
this.accountAssociationsRepository.delete(accountAssociations);
changes.put(DepositsApiConstants.linkedAccountParamName, null);
if (isLinkedAccRequired) {
this.depositAccountDataValidator.throwLinkedAccountRequiredError();
}
}
} else if (isLinkedAccRequired) {
this.depositAccountDataValidator.throwLinkedAccountRequiredError();
}
} else {
boolean isModified = false;
@@ -234,9 +234,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account);
boolean isInterestTransfer = false;
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(account, fmt, transactionDate,
transactionAmount, paymentDetail, true);
transactionAmount, paymentDetail, true, isInterestTransfer);
return new CommandProcessingResultBuilder() //
.withEntityId(withdrawal.getId()) //
@@ -277,8 +278,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN);
account.calculateInterestUsing(mc, today);
boolean isInterestTransfer = false;
account.calculateInterestUsing(mc, today, isInterestTransfer);
this.savingAccountRepository.save(account);
@@ -315,8 +316,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
account.postInterest(mc, today);
boolean isInterestTransfer = false;
account.postInterest(mc, today, isInterestTransfer);
// for generating transaction id's
List<SavingsAccountTransaction> transactions = account.getTransactions();
@@ -378,13 +379,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
account.undoTransaction(transactionId + 1);
}
}
boolean isInterestTransfer = false;
checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction);
account.activateAccountBasedOnBalance();
@@ -443,6 +444,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
}
SavingsAccountTransaction transaction = null;
boolean isInterestTransfer = false;
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, savingsAccountTransaction.createdDate());
if (savingsAccountTransaction.isDeposit()) {
@@ -454,9 +456,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction);
account.activateAccountBasedOnBalance();
@@ -541,7 +543,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccount.getTransactions().add(newTransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate);
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -564,7 +567,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccount.getTransactions().add(withdrawtransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate);
boolean isInterestTransfer = false;
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -597,9 +601,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (fieldOfficer != null) {
savingsAccount.update(fieldOfficer);
}
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
savingsAccount.calculateInterestUsing(mc, transferDate);
savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer);
this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
@@ -736,14 +740,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.waiveCharge(savingsAccountChargeId);
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction);
@@ -852,14 +856,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter);
boolean isInterestTransfer = false;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today);
account.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today);
account.calculateInterestUsing(mc, today, isInterestTransfer);
}
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode());