diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 6b2cd40ce..88af91609 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -19281,7 +19281,7 @@ No Request Body:
- +
Optional Fields
accountNo, externalId, fieldOfficerId,linkAccountId
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 )

diff --git a/mifosng-db/migrations/core_db/V168__added_transfer_fixed_deposit_interest_to_linked_account.sql b/mifosng-db/migrations/core_db/V168__added_transfer_fixed_deposit_interest_to_linked_account.sql new file mode 100755 index 000000000..9f03d7854 --- /dev/null +++ b/mifosng-db/migrations/core_db/V168__added_transfer_fixed_deposit_interest_to_linked_account.sql @@ -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); \ No newline at end of file diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/FixedDepositTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/FixedDepositTest.java index fb8dd4431..1876cabb2 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/FixedDepositTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/FixedDepositTest.java @@ -506,9 +506,6 @@ public class FixedDepositTest { this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec); - FixedDepositAccountHelper fixedDepositAccountHelperValidationError = new FixedDepositAccountHelper(this.requestSpec, - new ResponseSpecBuilder().build()); - DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); DateFormat monthDayFormat = new SimpleDateFormat("dd MMM"); diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/SchedulerJobsTestResults.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/SchedulerJobsTestResults.java index 973f9e329..65af04720 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/SchedulerJobsTestResults.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/SchedulerJobsTestResults.java @@ -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 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); + } } diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java index be314127e..8faeb2538 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java @@ -49,7 +49,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"; @@ -83,6 +83,7 @@ public class FixedDepositAccountHelper { 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) { @@ -162,6 +163,7 @@ public class FixedDepositAccountHelper { 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); @@ -432,4 +434,15 @@ public class FixedDepositAccountHelper { 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; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java index db614f928..82c6f29d7 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java @@ -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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferType.java index 4e67e35d4..3b19671a3 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferType.java @@ -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()); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java index 31fe4ed65..7c84ecccc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java @@ -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 retrieveByStandingInstruction(Long id, SearchParameters searchParameters); + + Collection fetchPostInterestTransactionIds(Long accountId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java index 1090afaab..201f8af59 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java @@ -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 retrieveAll(final SearchParameters searchParameters,final Long accountDetailId) { + public Page 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 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 transactionId = this.jdbcTemplate.queryForList(sql, Long.class, accountId, + AccountTransferType.INTEREST_TRANSFER.getValue()); + + return transactionId; + } + private static final class AccountTransfersMapper implements RowMapper { private final String schemaSql; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java index e02036e23..983c3333e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java @@ -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(), diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositsApiConstants.java index 05260989e..074e15f71 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/DepositsApiConstants.java @@ -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 DEPOSIT_TERM_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(minDepositTermParamName, maxDepositTermParamName, minDepositTermTypeIdParamName, maxDepositTermTypeIdParamName, inMultiplesOfDepositTermParamName, - inMultiplesOfDepositTermTypeIdParamName, depositAmountParamName, - depositMinAmountParamName, depositMaxAmountParamName)); + inMultiplesOfDepositTermTypeIdParamName, depositAmountParamName, depositMinAmountParamName, depositMaxAmountParamName)); private static final Set DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(minDepositTermParamName, maxDepositTermParamName, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTermParamName, - inMultiplesOfDepositTermType, depositAmountParamName, - depositMinAmountParamName, depositMaxAmountParamName)); + inMultiplesOfDepositTermType, depositAmountParamName, depositMinAmountParamName, depositMaxAmountParamName)); private static final Set RECURRING_DETAILS_REQUEST_DATA_PARAMETERS = new HashSet(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; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountDataValidator.java index 0ea946cf3..97a13a947 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/DepositAccountDataValidator.java @@ -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 dataValidationErrors = new ArrayList(); + 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()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositAccountData.java index a6b34fca0..bc3fec635 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/FixedDepositAccountData.java @@ -49,6 +49,7 @@ public class FixedDepositAccountData extends DepositAccountData { private EnumOptionData onAccountClosure; private final PortfolioAccountData linkedAccount; + private final Boolean transferInterestToSavings; private Collection interestFreePeriodTypeOptions; private Collection 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 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 interestFreePeriodTypeOptions = null; final Collection 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 interestFreePeriodTypeOptions = null; final Collection 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 depositPeriodFrequencyOptions, final EnumOptionData depositType, final EnumOptionData onAccountClosure, final Collection onAccountClosureOptions, final Collection paymentTypeOptions, final Collection 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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java index 084b4db31..67a4d27cb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java @@ -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, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java index 4be91950c..d829da2f1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java @@ -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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountTermAndPreClosure.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountTermAndPreClosure.java index 46149de9e..eee1b0488 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountTermAndPreClosure.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountTermAndPreClosure.java @@ -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 { @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 { 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 { this.account = account; this.expectedFirstDepositOnDate = expectedFirstDepositOnDate == null ? null : expectedFirstDepositOnDate.toDate(); this.onAccountClosureType = (accountOnClosureType == null) ? null : accountOnClosureType.getValue(); + this.transferInterestToLinkedAccount = transferInterest; } public Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { @@ -124,10 +129,10 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable { 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 { 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 { 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; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccount.java index 6dabb1909..f02d629a6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/FixedDepositAccount.java @@ -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 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 postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate); + final boolean isInterestTransfer = false; + final List 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 calculateInterestUsing(final MathContext mc, final LocalDate postingDate) { + public List 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(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccount.java index 4c001e520..36c72f761 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/RecurringDepositAccount.java @@ -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 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 postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate); + boolean isInterestTransfer = false; + final List 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 calculateInterestUsing(final MathContext mc, final LocalDate postingDate) { + public List 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(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 9c7dc3e14..906de0c8a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -370,9 +370,9 @@ public class SavingsAccount extends AbstractPersistable { 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 postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate); + final List postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer); Money interestPostedToDate = Money.zero(this.currency); @@ -447,8 +447,9 @@ public class SavingsAccount extends AbstractPersistable { * 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 calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate) { + public List 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 { final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, upToInterestCalculationDate); + final Collection 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 { 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 { public DepositAccountType depositAccountType() { return DepositAccountType.fromInt(depositType); } + + protected boolean isTransferInterestToOtherAccount() { + return false; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index ee48b48f4..a437cca99 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -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); } /** diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java index 30e0394ce..a16fad418 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java @@ -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); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java index ad39f6402..f3b7287c8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java @@ -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 existingTransactionIds = new HashSet(); final Set existingReversedTransactionIds = new HashSet(); @@ -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 existingTransactionIds = new HashSet(); final Set existingReversedTransactionIds = new HashSet(); 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); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index 9c4367bd4..ce0b09ae3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -445,13 +445,15 @@ public final class SavingsAccountTransaction extends AbstractPersistable { 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); + } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java index 15e14d8eb..656955661 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java @@ -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 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 allPeriods) { - return this.compoundInterestHelper.calculateInterestForAllPostingPeriods(currency, allPeriods); + public Money calculateInterestForAllPostingPeriods(final MonetaryCurrency currency, final List allPeriods, + LocalDate accountLockedUntil, Boolean immediateWithdrawalOfInterest) { + return this.compoundInterestHelper.calculateInterestForAllPostingPeriods(currency, allPeriods, accountLockedUntil, + immediateWithdrawalOfInterest); + } + + public Collection fetchPostInterestTransactionIds(Long accountId) { + return this.accountTransfersReadPlatformService.fetchPostInterestTransactionIds(accountId); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/CompoundInterestHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/CompoundInterestHelper.java index 4bdd187dc..0ba75941b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/CompoundInterestHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/CompoundInterestHelper.java @@ -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 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 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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java index 06d948531..6a7c1e735 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java @@ -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 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 interestPostTransactions, boolean isInterestTransfer) { final List accountEndOfDayBalances = new ArrayList(); - + 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 compoundingPeriods) { + final List 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 compoundingPeriodsInPostingPeriod(final LocalDateInterval postingPeriodInterval, - final SavingsCompoundingInterestPeriodType interestPeriodType, final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { + final SavingsCompoundingInterestPeriodType interestPeriodType, final List allEndOfDayBalances, + final LocalDate upToInterestCalculationDate) { final List compoundingPeriods = new ArrayList(); @@ -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; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformService.java index 27a0c409a..f95cde9fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformService.java @@ -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 retrieveForMaturityUpdate(); SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(final Long accountId); + + Collection retrieveDataForInterestTransfer(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java index c38583dcc..088be592e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java @@ -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 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 { 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 { + + 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); + } + + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformService.java index 764139b43..88e2aecaa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformService.java @@ -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; } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index 534be73c7..b239775ca 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -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 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 existingReversedTransactionIds = new HashSet(); 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()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java index 022ff2cfa..ba51f3528 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java @@ -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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index d2787bba4..fcaa554cc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -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 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 existingReversedTransactionIds = new HashSet(); 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());