This is an automated email from the ASF dual-hosted git repository. avikg pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push: new bd99138 FINERACT-893-fixed-deposit-rollover new 50d5d08 Merge pull request #769 from fynmanoj/findevfdrollover bd99138 is described below commit bd99138aadbb184f0ec563c8111cc79f132ee98d Author: Manoj <ma...@fynarfin.io> AuthorDate: Sat May 23 22:52:39 2020 +0530 FINERACT-893-fixed-deposit-rollover --- .../integrationtests/FixedDepositTest.java | 180 +++++++++++++++++++++ .../fixeddeposit/FixedDepositAccountHelper.java | 7 + .../infrastructure/core/api/JsonCommand.java | 23 +++ .../AccountAssociationsReadPlatformService.java | 2 + ...AccountAssociationsReadPlatformServiceImpl.java | 5 + .../savings/DepositAccountOnClosureType.java | 19 ++- .../portfolio/savings/DepositsApiConstants.java | 5 +- .../api/FixedDepositAccountsApiResource.java | 7 +- .../savings/data/DepositAccountDataValidator.java | 29 ++++ .../savings/data/FixedDepositAccountData.java | 101 +++++++----- .../savings/domain/DepositAccountAssembler.java | 12 +- .../domain/DepositAccountDomainService.java | 9 +- .../domain/DepositAccountDomainServiceJpa.java | 88 +++++++++- .../domain/DepositAccountTermAndPreClosure.java | 37 ++++- .../savings/domain/FixedDepositAccount.java | 30 ++++ .../savings/domain/RecurringDepositAccount.java | 4 + .../portfolio/savings/domain/SavingsAccount.java | 1 - .../DepositAccountReadPlatformServiceImpl.java | 19 ++- ...countWritePlatformServiceJpaRepositoryImpl.java | 39 ++++- .../DepositsDropdownReadPlatformService.java | 2 + .../DepositsDropdownReadPlatformServiceImpl.java | 12 ++ .../savings/service/SavingsEnumerations.java | 11 +- .../V358__fixed_deposit_rollover_transfer.sql | 21 +++ 23 files changed, 597 insertions(+), 66 deletions(-) diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/FixedDepositTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/FixedDepositTest.java index 815c485..dc06538 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/FixedDepositTest.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/FixedDepositTest.java @@ -2400,6 +2400,175 @@ public class FixedDepositTest { Assert.assertEquals("Verifying Pre-Closure maturity amount", expectedPrematureAmount, maturityAmount); } + /*** + * Test case for Fixed Deposit Account rollover with maturity + * instruction as re invest maturity amount(principal+interest) + */ + @Test + public void testFixedDepositAccountWithRolloverMaturityAmount() { + this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec); + this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec); + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec); + + /*** + * Create GL Accounts for product account mapping + */ + final Account assetAccount = this.accountHelper.createAssetAccount(); + final Account incomeAccount = this.accountHelper.createIncomeAccount(); + final Account expenseAccount = this.accountHelper.createExpenseAccount(); + final Account liabilityAccount = this.accountHelper.createLiabilityAccount(); + + DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US); + DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US); + DateFormat currentDateFormat = new SimpleDateFormat("dd"); + + 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, -1); + 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()); + final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime()); + + Integer currentDate = Integer.valueOf(currentDateFormat.format(todaysDate.getTime())); + Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE); + Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1; + todaysDate.add(Calendar.DATE, numberOfDaysLeft); + final String INTEREST_POSTED_DATE = dateFormat.format(todaysDate.getTime()); + final String CLOSED_ON_DATE = dateFormat.format(Calendar.getInstance().getTime()); + + Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); + Assert.assertNotNull(clientId); + + /*** + * Create FD product with CashBased accounting enabled + */ + final String accountingRule = CASH_BASED; + Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule, assetAccount, liabilityAccount, + incomeAccount, expenseAccount); + Assert.assertNotNull(fixedDepositProductId); + + /*** + * Set maturityInstructionId as re-invest principal+interest + * */ + final Integer maturityInstructionId = 300; + + /*** + * Apply for FD account with created product and verify status + */ + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM, maturityInstructionId); + + Assert.assertNotNull(fixedDepositAccountId); + + HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, + this.responseSpec, fixedDepositAccountId.toString()); + FixedDepositAccountStatusChecker.verifyFixedDepositIsPending(fixedDepositAccountStatusHashMap); + + /*** + * Approve the FD account and verify whether account is approved + */ + fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId, APPROVED_ON_DATE); + FixedDepositAccountStatusChecker.verifyFixedDepositIsApproved(fixedDepositAccountStatusHashMap); + + /*** + * Activate the FD Account and verify whether account is activated + */ + fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE); + FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap); + } + + + /*** + * Test case for Fixed Deposit Account rollover with maturity + * instruction as re invest principal only + */ + @Test + public void testFixedDepositAccountWithRolloverPrincipal() { + this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec); + this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec); + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec); + + /*** + * Create GL Accounts for product account mapping + */ + final Account assetAccount = this.accountHelper.createAssetAccount(); + final Account incomeAccount = this.accountHelper.createIncomeAccount(); + final Account expenseAccount = this.accountHelper.createExpenseAccount(); + final Account liabilityAccount = this.accountHelper.createLiabilityAccount(); + + DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US); + DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US); + DateFormat currentDateFormat = new SimpleDateFormat("dd"); + + 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, -1); + 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()); + final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime()); + + Integer currentDate = Integer.valueOf(currentDateFormat.format(todaysDate.getTime())); + Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE); + Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1; + todaysDate.add(Calendar.DATE, numberOfDaysLeft); + final String INTEREST_POSTED_DATE = dateFormat.format(todaysDate.getTime()); + final String CLOSED_ON_DATE = dateFormat.format(Calendar.getInstance().getTime()); + + Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); + Assert.assertNotNull(clientId); + + /*** + * Create FD product with CashBased accounting enabled + */ + final String accountingRule = CASH_BASED; + Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule, assetAccount, liabilityAccount, + incomeAccount, expenseAccount); + Assert.assertNotNull(fixedDepositProductId); + + /*** + * Set maturityInstructionId as re-invest principal + * */ + final Integer maturityInstructionId = 400; + + /*** + * Apply for FD account with created product and verify status + */ + Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE, + WHOLE_TERM, maturityInstructionId); + + Assert.assertNotNull(fixedDepositAccountId); + + HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec, + this.responseSpec, fixedDepositAccountId.toString()); + FixedDepositAccountStatusChecker.verifyFixedDepositIsPending(fixedDepositAccountStatusHashMap); + + /*** + * Approve the FD account and verify whether account is approved + */ + fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId, APPROVED_ON_DATE); + FixedDepositAccountStatusChecker.verifyFixedDepositIsApproved(fixedDepositAccountStatusHashMap); + + /*** + * Activate the FD Account and verify whether account is activated + */ + fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE); + FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap); + } + private Integer createFixedDepositProduct(final String validFrom, final String validTo, final String accountingRule, Account... accounts) { LOG.info("------------------------------CREATING NEW FIXED DEPOSIT PRODUCT ---------------------------------------"); @@ -2471,6 +2640,17 @@ public class FixedDepositTest { } private Integer applyForFixedDepositApplication(final String clientID, final String productID, final String submittedOnDate, + final String penalInterestType, final Integer maturityInstructionId) { + LOG.info("--------------------------------APPLYING FOR FIXED DEPOSIT ACCOUNT --------------------------------"); + final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec) // + .withSubmittedOnDate(submittedOnDate) + .withMaturityInstructionId(maturityInstructionId) + .build(clientID, productID, penalInterestType); + return this.fixedDepositAccountHelper + .applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec); + } + + private Integer applyForFixedDepositApplication(final String clientID, final String productID, final String submittedOnDate, final String penalInterestType, final String depositAmount, final String depositPeriod) { LOG.info("--------------------------------APPLYING FOR FIXED DEPOSIT ACCOUNT --------------------------------"); final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec) diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java index ded5afb..fa4c50b 100644 --- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java @@ -96,6 +96,7 @@ public class FixedDepositAccountHelper { private String submittedOnDate = ""; private String savingsId = null; private boolean transferInterest = false; + private Integer maturityInstructionId; public String build(final String clientId, final String productId, final String penalInterestType) { final HashMap<String, Object> map = new HashMap<>(); @@ -127,6 +128,7 @@ public class FixedDepositAccountHelper { map.put("submittedOnDate", this.submittedOnDate); map.put("linkAccountId", savingsId); map.put("transferInterestToSavings", transferInterest); + map.put("maturityInstructionId", maturityInstructionId); String fixedDepositAccountJson = new Gson().toJson(map); LOG.info("{}", fixedDepositAccountJson); @@ -489,4 +491,9 @@ public class FixedDepositAccountHelper { } return this.newDepositAmount; } + + public FixedDepositAccountHelper withMaturityInstructionId(Integer maturityInstructionId){ + this.maturityInstructionId = maturityInstructionId; + return this; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java index 7ca8d3a..5008fde 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java @@ -131,6 +131,10 @@ public final class JsonCommand { return new JsonCommand(resourceId, parsedCommand); } + public static JsonCommand fromJsonElement(final Long resourceId, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper) { + return new JsonCommand(resourceId, parsedCommand, fromApiJsonHelper); + } + public JsonCommand(final Long resourceId, final JsonElement parsedCommand) { this.parsedCommand = parsedCommand; this.resourceId = resourceId; @@ -150,6 +154,25 @@ public final class JsonCommand { this.organisationCreditBureauId=null; } + public JsonCommand(final Long resourceId, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper) { + this.parsedCommand = parsedCommand; + this.resourceId = resourceId; + this.commandId = null; + this.jsonCommand = null; + this.fromApiJsonHelper = fromApiJsonHelper; + this.entityName = null; + this.subresourceId = null; + this.groupId = null; + this.clientId = null; + this.loanId = null; + this.savingsId = null; + this.transactionId = null; + this.url = null; + this.productId = null; + this.creditBureauId=null; + this.organisationCreditBureauId=null; + } + public Long getOrganisationCreditBureauId() { return this.organisationCreditBureauId; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountAssociationsReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountAssociationsReadPlatformService.java index aad6f34..2ae6eda 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountAssociationsReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountAssociationsReadPlatformService.java @@ -31,4 +31,6 @@ public interface AccountAssociationsReadPlatformService { public PortfolioAccountData retriveSavingsLinkedAssociation(final Long savingsId); public Collection<AccountAssociationsData> retriveLoanAssociations(Long loanId, Integer associationType); + + PortfolioAccountData retriveSavingsAccount(Long savingsId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountAssociationsReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountAssociationsReadPlatformServiceImpl.java index 8c0bfa4..9f6a9bb 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountAssociationsReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountAssociationsReadPlatformServiceImpl.java @@ -191,4 +191,9 @@ public class AccountAssociationsReadPlatformServiceImpl implements AccountAssoci } } + @Override + public PortfolioAccountData retriveSavingsAccount(final Long savingsId){ + String accountNo = jdbcTemplate.queryForObject("select account_no from m_savings_account where id = ?", String.class, savingsId); + return PortfolioAccountData.lookup(savingsId, accountNo); + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/DepositAccountOnClosureType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/DepositAccountOnClosureType.java index 29ccd73..fc7a2dc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/DepositAccountOnClosureType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/DepositAccountOnClosureType.java @@ -32,7 +32,8 @@ public enum DepositAccountOnClosureType { INVALID(0, "depositAccountClosureType.invalid"), // WITHDRAW_DEPOSIT(100, "depositAccountClosureType.withdrawDeposit"), // TRANSFER_TO_SAVINGS(200, "depositAccountClosureType.transferToSavings"), // - REINVEST(300, "depositAccountClosureType.reinvest"); // + REINVEST_PRINCIPAL_AND_INTEREST(300, "depositAccountClosureType.reinvestPrincipalAndInterest"), + REINVEST_PRINCIPAL_ONLY(400, "depositAccountClosureType.reinvestPrincipalOnly"); // private final Integer value; private final String code; @@ -63,7 +64,10 @@ public enum DepositAccountOnClosureType { accountOnClosureType = DepositAccountOnClosureType.TRANSFER_TO_SAVINGS; break; case 300: - accountOnClosureType = DepositAccountOnClosureType.REINVEST; + accountOnClosureType = DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST; + break; + case 400: + accountOnClosureType = DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY; break; } return accountOnClosureType; @@ -78,7 +82,16 @@ public enum DepositAccountOnClosureType { } public boolean isReinvest() { - return this.value.equals(DepositAccountOnClosureType.REINVEST.getValue()); + return this.value.equals(DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST.getValue()) || + this.value.equals(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getValue()); + } + + public boolean isReinvestPrincipal() { + return this.value.equals(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getValue()); + } + + public boolean isReinvestPrincipalAndInterest() { + return this.value.equals(DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST.getValue()); } public boolean isInvalid() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/DepositsApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/DepositsApiConstants.java index b667fcc..44266f1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/DepositsApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/DepositsApiConstants.java @@ -190,6 +190,8 @@ public class DepositsApiConstants { public static final String transferDescriptionParamName = "transferDescription"; public static final String toSavingsAccountIdParamName = "toSavingsAccountId"; public static final String savingsAccounts = "savingsAccounts"; + public static final String maturityInstructionIdParamName = "maturityInstructionId"; + public static final String transferToSavingsIdParamName = "transferToSavingsId"; public static final String preMatureCloseOnDateParamName = "preMatureCloseOnDate"; @@ -298,7 +300,8 @@ public class DepositsApiConstants { interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, chargesParamName, chartsParamName, depositAmountParamName, depositPeriodParamName, depositPeriodFrequencyIdParamName, - savingsAccounts, expectedFirstDepositOnDateParamName, SavingsApiConstants.withHoldTaxParamName)); + savingsAccounts, expectedFirstDepositOnDateParamName, SavingsApiConstants.withHoldTaxParamName, + maturityInstructionIdParamName, transferToSavingsIdParamName)); public static final Set<String> FIXED_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS = fixedDepositAccountRequestData(); public static final Set<String> FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS = fixedDepositAccountResponseData(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/FixedDepositAccountsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/FixedDepositAccountsApiResource.java index c8ffc35..fa67b79 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/FixedDepositAccountsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/FixedDepositAccountsApiResource.java @@ -239,6 +239,7 @@ public class FixedDepositAccountsApiResource { Collection<SavingsAccountTransactionData> transactions = null; Collection<SavingsAccountChargeData> charges = null; PortfolioAccountData linkedAccount = null; + PortfolioAccountData transferToSavingsAccount = null; final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); if (!associationParameters.isEmpty()) { @@ -272,6 +273,10 @@ public class FixedDepositAccountsApiResource { } } + if(savingsAccount.getTransferToSavingsId() !=null){ + transferToSavingsAccount = this.accountAssociationsReadPlatformService.retriveSavingsAccount(savingsAccount.getTransferToSavingsId()); + } + FixedDepositAccountData templateData = null; final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); @@ -281,7 +286,7 @@ public class FixedDepositAccountsApiResource { staffInSelectedOfficeOnly); } - return FixedDepositAccountData.associationsAndTemplate(savingsAccount, templateData, transactions, charges, linkedAccount); + return FixedDepositAccountData.associationsAndTemplate(savingsAccount, templateData, transactions, charges, linkedAccount, transferToSavingsAccount); } @PUT diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountDataValidator.java index b95c6c7..cc0a1bf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountDataValidator.java @@ -29,6 +29,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isCalen import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isMandatoryDepositParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.linkedAccountParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.mandatoryRecommendedDepositAmountParamName; +import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermTypeIdParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.minDepositTermParamName; @@ -39,6 +40,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClos import static org.apache.fineract.portfolio.savings.DepositsApiConstants.recurringFrequencyParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.recurringFrequencyTypeParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName; +import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferToSavingsIdParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountNoParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName; @@ -77,6 +79,7 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType; import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.DepositsApiConstants; import org.apache.fineract.portfolio.savings.PreClosurePenalInterestOnType; @@ -308,6 +311,19 @@ public class DepositAccountDataValidator { } else { baseDataValidator.reset().parameter(linkedAccountParamName).value(linkAccountId).ignoreIfNull().longGreaterThanZero(); } + + if (this.fromApiJsonHelper.parameterExists(maturityInstructionIdParamName, element)) { + final Integer depositRolloverId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + maturityInstructionIdParamName, element); + baseDataValidator.reset().parameter(maturityInstructionIdParamName).value(depositRolloverId).notNull() + .isOneOfTheseValues(DepositAccountOnClosureType.integerValues()); + + if(depositRolloverId.equals(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getValue())){ + final Long transferToSavingsId = this.fromApiJsonHelper.extractLongNamed( + transferToSavingsIdParamName, element); + baseDataValidator.reset().parameter(transferToSavingsIdParamName).value(transferToSavingsId).notNull().longGreaterThanZero(); + } + } } private void validateDepositDetailsForUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator) { @@ -439,6 +455,19 @@ public class DepositAccountDataValidator { } } + if (this.fromApiJsonHelper.parameterExists(maturityInstructionIdParamName, element)) { + final Integer depositRolloverId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed( + maturityInstructionIdParamName, element); + baseDataValidator.reset().parameter(maturityInstructionIdParamName).value(depositRolloverId).notNull() + .isOneOfTheseValues(DepositAccountOnClosureType.integerValues()); + + if(depositRolloverId.equals(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getValue())){ + final Long transferToSavingsId = this.fromApiJsonHelper.extractLongNamed( + transferToSavingsIdParamName, element); + baseDataValidator.reset().parameter(transferToSavingsIdParamName).value(transferToSavingsId).notNull().longGreaterThanZero(); + } + } + } private void validatePreClosureDetailForSubmit(final JsonElement element, final DataValidatorBuilder baseDataValidator) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/FixedDepositAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/FixedDepositAccountData.java index eb7a74d..63fccc1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/FixedDepositAccountData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/FixedDepositAccountData.java @@ -53,12 +53,14 @@ public class FixedDepositAccountData extends DepositAccountData { private Integer depositPeriod; private EnumOptionData depositPeriodFrequency; private BigDecimal activationCharge; + private Long transferToSavingsId; // used for account close private EnumOptionData onAccountClosure; private final PortfolioAccountData linkedAccount; private final Boolean transferInterestToSavings; + private final PortfolioAccountData transferToSavingsAccount; private Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions; private Collection<EnumOptionData> periodFrequencyTypeOptions; @@ -67,6 +69,7 @@ public class FixedDepositAccountData extends DepositAccountData { // for account close private Collection<EnumOptionData> onAccountClosureOptions; private Collection<PaymentTypeData> paymentTypeOptions; + private final Collection<EnumOptionData> maturityInstructionOptions; //import fields private transient Integer rowIndex; @@ -114,6 +117,7 @@ public class FixedDepositAccountData extends DepositAccountData { this.activationCharge = null; this.onAccountClosure = null; this.linkedAccount = null; + this.transferToSavingsAccount = null; this.transferInterestToSavings = null; this.preClosurePenalInterestOnTypeOptions = null; this.periodFrequencyTypeOptions = null; @@ -125,6 +129,7 @@ public class FixedDepositAccountData extends DepositAccountData { this.locale= locale; this.submittedOnDate = submittedOnDate; this.depositPeriodFrequencyId = depositPeriodFrequencyId; + this.maturityInstructionOptions = null; } public Integer getRowIndex() { @@ -136,11 +141,14 @@ public class FixedDepositAccountData extends DepositAccountData { final Integer maxDepositTerm, 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 Boolean transferInterestToSavings) { + final EnumOptionData depositPeriodFrequency, final EnumOptionData onAccountClosure, final Boolean transferInterestToSavings, + final Long transferToSavingsId) { final PortfolioAccountData linkedAccount = null; + final PortfolioAccountData transferToSavingsAccount = null; final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null; final Collection<EnumOptionData> periodFrequencyTypeOptions = null; + final Collection<EnumOptionData> maturityInstructionOptions = null; final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue()); final Collection<EnumOptionData> onAccountClosureOptions = null; @@ -166,7 +174,7 @@ public class FixedDepositAccountData extends DepositAccountData { maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount, transferInterestToSavings, depositAccountData.withHoldTax, - depositAccountData.taxGroup); + depositAccountData.taxGroup, maturityInstructionOptions, transferToSavingsId, transferToSavingsAccount); } public static FixedDepositAccountData withInterestChart(final FixedDepositAccountData account, @@ -187,12 +195,13 @@ public class FixedDepositAccountData extends DepositAccountData { account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts, - account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup); + account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup, account.maturityInstructionOptions, + account.transferToSavingsId, account.transferToSavingsAccount); } public static FixedDepositAccountData associationsAndTemplate(final FixedDepositAccountData account, FixedDepositAccountData template, final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges, - final PortfolioAccountData linkedAccount) { + final PortfolioAccountData linkedAccount, PortfolioAccountData transferToSavingsAccount ) { if (template == null) { template = account; @@ -214,7 +223,8 @@ public class FixedDepositAccountData extends DepositAccountData { account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, template.periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, template.savingsAccounts, - linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup); + linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup, account.maturityInstructionOptions, + account.transferToSavingsId, transferToSavingsAccount); } public static FixedDepositAccountData withTemplateOptions(final FixedDepositAccountData account, @@ -226,7 +236,8 @@ public class FixedDepositAccountData extends DepositAccountData { final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions, final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions, - final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<SavingsAccountData> savingsAccounts) { + final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<SavingsAccountData> savingsAccounts, + final Collection<EnumOptionData> maturityInstructionOptions) { return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName, account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId, @@ -243,7 +254,8 @@ public class FixedDepositAccountData extends DepositAccountData { account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, savingsAccounts, - account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup); + account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup, maturityInstructionOptions, + account.transferToSavingsId, account.transferToSavingsAccount); } public static FixedDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, @@ -304,6 +316,7 @@ public class FixedDepositAccountData extends DepositAccountData { final EnumOptionData depositPeriodFrequency = null; final EnumOptionData onAccountClosure = null; final PortfolioAccountData linkedAccount = null; + final PortfolioAccountData transferToSavingsAccount = null; final Boolean transferInterestToSavings = null; final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null; final Collection<EnumOptionData> periodFrequencyTypeOptions = null; @@ -312,6 +325,8 @@ public class FixedDepositAccountData extends DepositAccountData { final Collection<EnumOptionData> onAccountClosureOptions = null; final Collection<PaymentTypeData> paymentTypeOptions = null; final Collection<SavingsAccountData> savingsAccountDatas = null; + final Collection<EnumOptionData> maturityInstructionOptions = null; + final Long transferToSavingsId = null; return new FixedDepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, @@ -324,7 +339,7 @@ public class FixedDepositAccountData extends DepositAccountData { maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount, - transferInterestToSavings, withHoldTax, taxGroup); + transferInterestToSavings, withHoldTax, taxGroup, maturityInstructionOptions, transferToSavingsId, transferToSavingsAccount); } public static FixedDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount, @@ -393,6 +408,9 @@ public class FixedDepositAccountData extends DepositAccountData { final PortfolioAccountData linkedAccount = null; final boolean withHoldTax = false; final TaxGroupData taxGroup = null; + final Collection<EnumOptionData> maturityInstructionOptions = null; + final Long transferToSavingsId = null; + final PortfolioAccountData transferToSavingsAccount = null; return new FixedDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, @@ -405,7 +423,7 @@ public class FixedDepositAccountData extends DepositAccountData { maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount, - transferInterestToSavings, withHoldTax, taxGroup); + transferInterestToSavings, withHoldTax, taxGroup, maturityInstructionOptions, transferToSavingsId, transferToSavingsAccount); } public static FixedDepositAccountData withClosureTemplateDetails(final FixedDepositAccountData account, @@ -428,38 +446,40 @@ public class FixedDepositAccountData extends DepositAccountData { account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate, account.depositPeriod, account.depositPeriodFrequency, account.periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, account.linkedAccount, - account.transferInterestToSavings, account.withHoldTax, account.taxGroup); + account.transferInterestToSavings, account.withHoldTax, account.taxGroup, account.maturityInstructionOptions, + account.transferToSavingsId, account.transferToSavingsAccount); } private FixedDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, - final String groupName, final Long clientId, final String clientName, final Long productId, final String productName, - final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status, - final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, - final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType, - final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, - final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, - final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, - final BigDecimal minBalanceForInterestCalculation, final SavingsAccountSummaryData summary, - final Collection<SavingsAccountTransactionData> transactions, final Collection<DepositProductData> productOptions, - final Collection<StaffData> fieldOfficerOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions, - final Collection<EnumOptionData> interestPostingPeriodTypeOptions, - final Collection<EnumOptionData> interestCalculationTypeOptions, - final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions, - final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions, - final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions, - final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate, - final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest, - final EnumOptionData preClosurePenalInterestOnType, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions, - final Integer minDepositTerm, final Integer maxDepositTerm, 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 Collection<EnumOptionData> periodFrequencyTypeOptions, final EnumOptionData depositType, - final EnumOptionData onAccountClosure, final Collection<EnumOptionData> onAccountClosureOptions, - final Collection<PaymentTypeData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas, - final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings, final boolean withHoldTax, - final TaxGroupData taxGroup) { + final String groupName, final Long clientId, final String clientName, final Long productId, final String productName, + final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status, + final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, + final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType, + final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, + final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, + final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, + final BigDecimal minBalanceForInterestCalculation, final SavingsAccountSummaryData summary, + final Collection<SavingsAccountTransactionData> transactions, final Collection<DepositProductData> productOptions, + final Collection<StaffData> fieldOfficerOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions, + final Collection<EnumOptionData> interestPostingPeriodTypeOptions, + final Collection<EnumOptionData> interestCalculationTypeOptions, + final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions, + final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions, + final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions, + final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate, + final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest, + final EnumOptionData preClosurePenalInterestOnType, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions, + final Integer minDepositTerm, final Integer maxDepositTerm, 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 Collection<EnumOptionData> periodFrequencyTypeOptions, final EnumOptionData depositType, + final EnumOptionData onAccountClosure, final Collection<EnumOptionData> onAccountClosureOptions, + final Collection<PaymentTypeData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas, + final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings, final boolean withHoldTax, + final TaxGroupData taxGroup, final Collection<EnumOptionData> maturityInstructionOptions, final Long transferToSavingsId, + final PortfolioAccountData transferToSavingsAccount) { super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId, fieldofficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType, @@ -485,6 +505,7 @@ public class FixedDepositAccountData extends DepositAccountData { this.depositPeriodFrequency = depositPeriodFrequency; this.onAccountClosure = onAccountClosure; this.linkedAccount = linkedAccount; + this.transferToSavingsAccount = transferToSavingsAccount; this.transferInterestToSavings = transferInterestToSavings; // template @@ -495,6 +516,8 @@ public class FixedDepositAccountData extends DepositAccountData { this.onAccountClosureOptions = onAccountClosureOptions; this.paymentTypeOptions = paymentTypeOptions; this.savingsAccounts = savingsAccountDatas; + this.maturityInstructionOptions = maturityInstructionOptions; + this.transferToSavingsId = transferToSavingsId; } @Override @@ -521,4 +544,8 @@ public class FixedDepositAccountData extends DepositAccountData { public void setActivationCharge(BigDecimal activationCharge) { this.activationCharge = activationCharge; } + + public Long getTransferToSavingsId() { + return transferToSavingsId; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountAssembler.java index 6591ec3..283bfd5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountAssembler.java @@ -32,7 +32,9 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.expecte import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isCalendarInheritedParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isMandatoryDepositParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.mandatoryRecommendedDepositAmountParamName; +import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName; +import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferToSavingsIdParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountNoParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.clientIdParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.externalIdParamName; @@ -298,6 +300,10 @@ public class DepositAccountAssembler { throw new UnsupportedParameterException(Arrays.asList(withHoldTaxParamName)); } } + Integer depositRolloverId = null; + if(command.parameterExists(maturityInstructionIdParamName)){ + depositRolloverId = command.integerValueOfParameterNamed(maturityInstructionIdParamName); + } SavingsAccount account = null; if (depositAccountType.isFixedDeposit()) { @@ -375,10 +381,12 @@ public class DepositAccountAssembler { final BigDecimal maturityAmount = null;// calculated and updated in // account final LocalDate maturityDate = null;// calculated and updated in account - final DepositAccountOnClosureType accountOnClosureType = null; + final Integer accountOnClosureTypeId = command.integerValueOfParameterNamed(maturityInstructionIdParamName); + final DepositAccountOnClosureType accountOnClosureType = accountOnClosureTypeId != null ? DepositAccountOnClosureType.fromInt(accountOnClosureTypeId) :null; + final Long transferToSavingsId = command.longValueOfParameterNamed(transferToSavingsIdParamName); return DepositAccountTermAndPreClosure.createNew(updatedProductPreClosure, updatedProductTerm, account, depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType, - trasferInterest); + trasferInterest, transferToSavingsId); } public DepositAccountRecurringDetail assembleAccountRecurringDetail(final JsonCommand command, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainService.java index 08f602e..40d4a5f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainService.java @@ -25,6 +25,7 @@ import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.useradministration.domain.AppUser; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; +import org.springframework.transaction.annotation.Transactional; public interface DepositAccountDomainService { @@ -43,8 +44,14 @@ public interface DepositAccountDomainService { Long handleFDAccountClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, LocalDate tenantsTodayDate, Map<String, Object> changes); + @Transactional + Long handleFDAccountMaturityClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, + LocalDate tenantsTodayDate, DateTimeFormatter fmt, + LocalDate closedDate, Integer onAccountClosureId, + Long toSavingsId, String transferDescription, Map<String, Object> changes); + Long handleRDAccountClosure(RecurringDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, - LocalDate tenantsTodayDate, Map<String, Object> changes); + LocalDate tenantsTodayDate, Map<String, Object> changes); Long handleFDAccountPreMatureClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, LocalDate tenantsTodayDate, Map<String, Object> changes); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java index e8b5d7f..c9a5f97 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java @@ -189,7 +189,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi @Transactional @Override public Long handleFDAccountClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user, - final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) { + final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); @@ -263,6 +263,85 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi @Transactional @Override + public Long handleFDAccountMaturityClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user, + final LocalDate tenantsTodayDate, final DateTimeFormatter fmt, + final LocalDate closedDate, final Integer onAccountClosureId, + final Long toSavingsId, final String transferDescription, + Map<String, Object> changes) { + + final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService + .isSavingsInterestPostingAtCurrentPeriodEnd(); + final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); + + boolean isRegularTransaction = false; + boolean isAccountTransfer = false; + final boolean isPreMatureClosure = false; + final Set<Long> existingTransactionIds = new HashSet<>(); + final Set<Long> existingReversedTransactionIds = new HashSet<>(); + /*** + * Update account transactionIds for post journal entries. + */ + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + final MathContext mc = MathContext.DECIMAL64; + Long savingsTransactionId = null; + account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); + final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); + if (onClosureType.isReinvest()) { + BigDecimal reInvestAmount; + if(onClosureType.isReinvestPrincipal()) + reInvestAmount = account.getDepositAmount(); + else + reInvestAmount = account.getAccountBalance(); + FixedDepositAccount reinvestedDeposit = account.reInvest(reInvestAmount); + this.depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit); + reinvestedDeposit.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure, + isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); + + this.savingsAccountRepository.save(reinvestedDeposit); + autoGenerateAccountNumber(reinvestedDeposit); + final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, reInvestAmount, + paymentDetail, false, isRegularTransaction); + savingsTransactionId = withdrawal.getId(); + + if (onClosureType.isReinvestPrincipalAndInterest()) { + account.updateClosedStatus(); + account.updateOnAccountClosureStatus(onClosureType); + } + changes.put("reinvestedDepositId", reinvestedDeposit.getId()); + reinvestedDeposit.approveAndActivateApplication(closedDate.toDate(), user); + this.savingsAccountRepository.save(reinvestedDeposit); + + } else if (onClosureType.isTransferToSavings()) { + final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, + DepositAccountType.SAVINGS_DEPOSIT); + final boolean isExceptionForBalanceCheck = false; + final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(closedDate, account.getAccountBalance(), + PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, null, null, transferDescription, null, fmt, null, null, + null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account, + isAccountTransfer, isExceptionForBalanceCheck); + this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); + updateAlreadyPostedTransactions(existingTransactionIds, account); + account.updateClosedStatus(); + account.updateOnAccountClosureStatus(onClosureType); + } else { + final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(), + paymentDetail, false, isRegularTransaction); + savingsTransactionId = withdrawal.getId(); + } + + //if(!processMaturityInstructionOnly) + // account.close(user, command, tenantsTodayDate, changes); + + this.savingsAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); + + return savingsTransactionId; + } + + @Transactional + @Override public Long handleRDAccountClosure(final RecurringDepositAccount account, final PaymentDetail paymentDetail, final AppUser user, final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) { @@ -290,7 +369,12 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName); final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); if (onClosureType.isReinvest()) { - RecurringDepositAccount reinvestedDeposit = account.reInvest(transactionAmount); + BigDecimal reInvestAmount; + if(onClosureType.isReinvestPrincipal()) + reInvestAmount = account.getDepositAmount(); + else + reInvestAmount = account.getAccountBalance(); + RecurringDepositAccount reinvestedDeposit = account.reInvest(reInvestAmount); depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit); this.savingsAccountRepository.save(reinvestedDeposit); final CalendarInstance calendarInstance = getCalendarInstance(account, reinvestedDeposit); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountTermAndPreClosure.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountTermAndPreClosure.java index 3b833a5..f13a1b4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountTermAndPreClosure.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountTermAndPreClosure.java @@ -24,7 +24,9 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.deposit import static org.apache.fineract.portfolio.savings.DepositsApiConstants.depositPeriodParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.localeParamName; +import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName; import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName; +import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferToSavingsIdParamName; import java.math.BigDecimal; import java.util.Date; @@ -90,6 +92,9 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom { @Column(name = "transfer_interest_to_linked_account", nullable = false) private boolean transferInterestToLinkedAccount; + @Column(name = "transfer_to_savings_account_id") + private Long transferToSavingsAccountId; + protected DepositAccountTermAndPreClosure() { super(); } @@ -97,16 +102,16 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom { 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, Boolean trasferInterest) { + final DepositAccountOnClosureType accountOnClosureType, Boolean trasferInterest, Long transferToSavingsId) { return new DepositAccountTermAndPreClosure(preClosureDetail, depositTermDetail, account, depositAmount, maturityAmount, - maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType, trasferInterest); + maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType, trasferInterest, transferToSavingsId); } 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, Boolean transferInterest) { + final DepositAccountOnClosureType accountOnClosureType, Boolean transferInterest, Long transferToSavingsId) { this.depositAmount = depositAmount; this.maturityAmount = maturityAmount; this.maturityDate = (maturityDate == null) ? null : maturityDate.toDate(); @@ -118,6 +123,7 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom { this.expectedFirstDepositOnDate = expectedFirstDepositOnDate == null ? null : expectedFirstDepositOnDate.toDate(); this.onAccountClosureType = (accountOnClosureType == null) ? null : accountOnClosureType.getValue(); this.transferInterestToLinkedAccount = transferInterest; + this.transferToSavingsAccountId = transferToSavingsId; } public Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { @@ -158,6 +164,20 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom { this.transferInterestToLinkedAccount = newValue; } + if (command.isChangeInIntegerParameterNamed(maturityInstructionIdParamName, this.onAccountClosureType) || command.integerValueOfParameterNamed( + maturityInstructionIdParamName) == null) { + final Integer newValue = command.integerValueOfParameterNamed(maturityInstructionIdParamName); + actualChanges.put(maturityInstructionIdParamName, newValue); + this.onAccountClosureType = newValue != null ? DepositAccountOnClosureType.fromInt(newValue).getValue() : null; + } + + if (command.isChangeInLongParameterNamed(transferToSavingsIdParamName, this.transferToSavingsAccountId) || command.integerValueOfParameterNamed( + transferToSavingsIdParamName) == null) { + final Long newValue = command.longValueOfParameterNamed(transferToSavingsIdParamName); + actualChanges.put(transferToSavingsIdParamName, newValue); + this.transferToSavingsAccountId = newValue ; + } + if (this.preClosureDetail != null) { actualChanges.putAll(this.preClosureDetail.update(command, baseDataValidator)); } @@ -291,9 +311,10 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom { final Boolean transferInterestToLinkedAccount = false; final DepositAccountOnClosureType accountOnClosureType = null; + final Long transferToSavingsId = null; return DepositAccountTermAndPreClosure.createNew(preClosureDetail, depositTermDetail, account, actualDepositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType, - transferInterestToLinkedAccount); + transferInterestToLinkedAccount, transferToSavingsId); } public void updateExpectedFirstDepositDate(final LocalDate expectedFirstDepositOnDate) { @@ -311,4 +332,12 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom { } return isAfterExpectedFirstDepositDate; } + + public Integer getOnAccountClosureType() { + return onAccountClosureType; + } + + public Long getTransferToSavingsAccountId() { + return transferToSavingsAccountId; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java index 2c909ef..d5e23b9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java @@ -259,6 +259,8 @@ public class FixedDepositAccount extends SavingsAccount { } } + + public LocalDate calculateMaturityDate() { final LocalDate startDate = accountSubmittedOrActivationDate(); @@ -489,6 +491,14 @@ public class FixedDepositAccount extends SavingsAccount { // this.savingsAccountTransactionSummaryWrapper, this.transactions); } + public void updateClosedStatus(){ + this.status = SavingsAccountStatusType.CLOSED.getValue(); + } + + public void updateOnAccountClosureStatus(DepositAccountOnClosureType onClosureType){ + this.accountTermAndPreClosure.updateOnAccountClosureStatus(onClosureType); + } + public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final LocalDate interestPostingUpToDate = maturityDate(); final MathContext mc = MathContext.DECIMAL64; @@ -756,6 +766,14 @@ public class FixedDepositAccount extends SavingsAccount { return this.accountTermAndPreClosure.isTransferToSavingsOnClosure(); } + public Integer getOnAccountClosureId(){ + return this.accountTermAndPreClosure.getOnAccountClosureType(); + } + + public Long getTransferToSavingsAccountId() { + return this.accountTermAndPreClosure.getTransferToSavingsAccountId(); + } + public FixedDepositAccount reInvest(BigDecimal depositAmount) { final DepositAccountTermAndPreClosure newAccountTermAndPreClosure = this.accountTermAndPreClosure.copy(depositAmount); @@ -830,4 +848,16 @@ public class FixedDepositAccount extends SavingsAccount { super.loadLazyCollections(); this.chart.getId() ; } + + public BigDecimal getDepositAmount() { + return this.accountTermAndPreClosure.depositAmount(); + } + @Override + public BigDecimal getAccountBalance() { + return this.summary.getAccountBalance(this.currency).getAmount(); + } + public boolean isMatured(){ + return SavingsAccountStatusType.MATURED.getValue().equals(this.status); + } + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java index 79d592d..670d53c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java @@ -1232,4 +1232,8 @@ public class RecurringDepositAccount extends SavingsAccount { super.loadLazyCollections(); this.chart.getId() ; } + + public BigDecimal getDepositAmount() { + return this.accountTermAndPreClosure.depositAmount(); + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java index c52e676..a1499c1 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java @@ -1433,7 +1433,6 @@ public class SavingsAccount extends AbstractPersistableCustom { baseDataValidator.reset().parameter(withHoldTaxParamName).failWithCode("not.supported.for.this.account"); } } - validateLockinDetails(baseDataValidator); esnureOverdraftLimitsSetForOverdraftAccounts(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java index 1a70557..55b43fc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java @@ -378,6 +378,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead final boolean feeChargesOnly = false; final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly); + final Collection<EnumOptionData> maturityInstructionOptions = this.depositsDropdownReadPlatformService.maturityInstructionOptions(); + Collection<StaffData> fieldOfficerOptions = null; if (officeId != null) { @@ -405,7 +407,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead } // retrieve chart Slabs - final InterestRateChartData productChartData = this.productChartReadPlatformService.retrieveActiveChartWithTemplate(productId); + final InterestRateChartData productChartData = this.productChartReadPlatformService.retrieveActiveChartWithTemplate(productId); final DepositAccountInterestRateChartData accountChart = DepositAccountInterestRateChartData.from(productChartData); if (depositAccountType.isFixedDeposit()) { @@ -414,7 +416,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions, preClosurePenalInterestOnTypeOptions, - periodFrequencyTypeOptions, savingsAccountDatas); + periodFrequencyTypeOptions, savingsAccountDatas, maturityInstructionOptions); template = FixedDepositAccountData.withInterestChart((FixedDepositAccountData) template, accountChart); } else if (depositAccountType.isRecurringDeposit()) { @@ -450,6 +452,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead final Collection<SavingsAccountTransactionData> transactions = null; final Collection<SavingsAccountChargeData> charges = null; + final Collection<EnumOptionData> maturityInstructionOptions = null; final boolean feeChargesOnly = true; final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly); @@ -462,7 +465,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions, preClosurePenalInterestOnTypeOptions, - periodFrequencyTypeOptions, savingsAccountDatas); + periodFrequencyTypeOptions, savingsAccountDatas, maturityInstructionOptions); } else if (depositAccountType.isRecurringDeposit()) { template = RecurringDepositAccountData.withClientTemplate(clientId, clientName, groupId, groupName); @@ -778,7 +781,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead 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.transfer_interest_to_linked_account as transferInterestToSavings "); + sqlBuilder.append("datp.transfer_interest_to_linked_account as transferInterestToSavings, "); + sqlBuilder.append("datp.transfer_to_savings_account_id as transferToSavingsId "); sqlBuilder.append(super.selectTablesSql()); @@ -826,10 +830,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead .depositAccountOnClosureType(onAccountClosureId); final Boolean transferInterestToSavings = rs.getBoolean("transferInterestToSavings"); + final Long transferToSavingsId = JdbcSupport.getLong(rs, "transferToSavingsId"); + + return FixedDepositAccountData.instance(depositAccountData, preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, - depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings); + depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings, transferToSavingsId); } } @@ -1266,7 +1273,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead return FixedDepositAccountData.instance(depositAccountData, preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod, - depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings); + depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings, null); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index c1c5fc5..2e42c5c 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -28,6 +28,7 @@ import java.math.BigDecimal; import java.math.MathContext; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -44,6 +45,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; @@ -145,6 +147,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo private final HolidayRepositoryWrapper holidayRepository; private final WorkingDaysRepositoryWrapper workingDaysRepository; private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository; + private final FromJsonHelper fromApiJsonHelper; @Autowired public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -164,7 +167,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final AccountTransfersWritePlatformService accountTransfersWritePlatformService, final DepositAccountReadPlatformService depositAccountReadPlatformService, final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService, - final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository) { + final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository, + final FromJsonHelper fromApiJsonHelper) { this.context = context; this.savingAccountRepositoryWrapper = savingAccountRepositoryWrapper; @@ -188,6 +192,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo this.calendarInstanceRepository = calendarInstanceRepository; this.configurationDomainService = configurationDomainService; this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository; + this.fromApiJsonHelper = fromApiJsonHelper; } @Transactional @@ -798,14 +803,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo this.depositAccountTransactionDataValidator.validateClosing(command, DepositAccountType.FIXED_DEPOSIT, isPreMatureClose); final Map<String, Object> changes = new LinkedHashMap<>(); - final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + final PaymentDetail + paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); final FixedDepositAccount account = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId, DepositAccountType.FIXED_DEPOSIT); checkClientOrGroupActive(account); - this.depositAccountDomainService.handleFDAccountClosure(account, paymentDetail, user, command, DateUtils.getLocalDateOfTenant(), - changes); + this.depositAccountDomainService.handleFDAccountClosure(account, paymentDetail, user, command, DateUtils.getLocalDateOfTenant(), changes); final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { @@ -1352,6 +1357,32 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo if (depositAccountType.isFixedDeposit()) { ((FixedDepositAccount) account).updateMaturityStatus(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); + FixedDepositAccount fdAccount = ((FixedDepositAccount) account); + //handle maturity instructions + + if(fdAccount.isMatured() && (fdAccount.isReinvestOnClosure() || fdAccount.isTransferToSavingsOnClosure())){ + DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd"); + Map<String, Object> changes = new HashMap<>(); + AppUser user = context.authenticatedUser(); + Long toSavingsId = fdAccount.getTransferToSavingsAccountId(); + this.depositAccountDomainService.handleFDAccountMaturityClosure(fdAccount, null, user, + fdAccount.maturityDate(), fmt, fdAccount.maturityDate(), + fdAccount.getOnAccountClosureId(), toSavingsId, "Apply maturity instructions", changes); + + if(changes.get("reinvestedDepositId") != null) { + Long reinvestedDepositId = (Long) changes.get("reinvestedDepositId"); + Money amountForDeposit = account.activateWithBalance(); + final FixedDepositAccount reinvestAccount = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(reinvestedDepositId, + DepositAccountType.FIXED_DEPOSIT); + Money activationChargeAmount = getActivationCharge(reinvestAccount); + if(activationChargeAmount.isGreaterThanZero()){ + payActivationCharge(reinvestAccount, user); + amountForDeposit = amountForDeposit.plus(activationChargeAmount); + } + this.depositAccountDomainService.handleFDDeposit(reinvestAccount, fmt, fdAccount.maturityDate(), + amountForDeposit.getAmount(), null); + } + } } else if (depositAccountType.isRecurringDeposit()) { ((RecurringDepositAccount) account).updateMaturityStatus(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositsDropdownReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositsDropdownReadPlatformService.java index d186a69..07c041f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositsDropdownReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositsDropdownReadPlatformService.java @@ -27,6 +27,8 @@ public interface DepositsDropdownReadPlatformService { Collection<EnumOptionData> retrievePreClosurePenalInterestOnTypeOptions(); + Collection<EnumOptionData> maturityInstructionOptions(); + /* * Collection<EnumOptionData> retrieveDepositTermTypeOptions(); * diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositsDropdownReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositsDropdownReadPlatformServiceImpl.java index 70ce68f..7c172e0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositsDropdownReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositsDropdownReadPlatformServiceImpl.java @@ -18,8 +18,10 @@ */ package org.apache.fineract.portfolio.savings.service; +import java.util.Arrays; import java.util.Collection; import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType; import org.apache.fineract.portfolio.savings.PreClosurePenalInterestOnType; import org.springframework.stereotype.Service; @@ -48,4 +50,14 @@ public class DepositsDropdownReadPlatformServiceImpl implements DepositsDropdown * .recurringDepositFrequencyType(SavingsPeriodFrequencyType.values()); } */ + + @Override + public Collection<EnumOptionData> maturityInstructionOptions() { + return Arrays.asList( + SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType.WITHDRAW_DEPOSIT), + SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS), + SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST), + SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY)); + + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java index b552cbd..b0b2644 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java @@ -795,10 +795,14 @@ public class SavingsEnumerations { optionData = new EnumOptionData(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getValue().longValue(), DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getCode(), "Transfer to Savings"); break; - case REINVEST: - optionData = new EnumOptionData(DepositAccountOnClosureType.REINVEST.getValue().longValue(), - DepositAccountOnClosureType.REINVEST.getCode(), "Re-Invest"); + case REINVEST_PRINCIPAL_AND_INTEREST: + optionData = new EnumOptionData(DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST.getValue().longValue(), + DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST.getCode(), "Re-Invest Maturity Amount"); break; + case REINVEST_PRINCIPAL_ONLY: + optionData = new EnumOptionData(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getValue().longValue(), + DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getCode(), "Re-Invest Principal Only"); + break; } return optionData; } @@ -835,5 +839,4 @@ public class SavingsEnumerations { } return optionData; } - } \ No newline at end of file diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V358__fixed_deposit_rollover_transfer.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V358__fixed_deposit_rollover_transfer.sql new file mode 100644 index 0000000..6bb8300 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V358__fixed_deposit_rollover_transfer.sql @@ -0,0 +1,21 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +alter table m_deposit_account_term_and_preclosure add column transfer_to_savings_account_id bigint(20) DEFAULT NULL, +add CONSTRAINT FOREIGN KEY (`transfer_to_savings_account_id`) REFERENCES `m_savings_account` (`id`); \ No newline at end of file