From bd99138aadbb184f0ec563c8111cc79f132ee98d Mon Sep 17 00:00:00 2001 From: Manoj Date: Sat, 23 May 2020 22:52:39 +0530 Subject: [PATCH] FINERACT-893-fixed-deposit-rollover --- .../integrationtests/FixedDepositTest.java | 180 ++++++++++++++++++ .../FixedDepositAccountHelper.java | 7 + .../infrastructure/core/api/JsonCommand.java | 23 +++ ...ccountAssociationsReadPlatformService.java | 2 + ...ntAssociationsReadPlatformServiceImpl.java | 5 + .../savings/DepositAccountOnClosureType.java | 19 +- .../savings/DepositsApiConstants.java | 5 +- .../api/FixedDepositAccountsApiResource.java | 7 +- .../data/DepositAccountDataValidator.java | 29 +++ .../savings/data/FixedDepositAccountData.java | 101 ++++++---- .../domain/DepositAccountAssembler.java | 12 +- .../domain/DepositAccountDomainService.java | 9 +- .../DepositAccountDomainServiceJpa.java | 88 ++++++++- .../DepositAccountTermAndPreClosure.java | 37 +++- .../savings/domain/FixedDepositAccount.java | 30 +++ .../domain/RecurringDepositAccount.java | 4 + .../savings/domain/SavingsAccount.java | 1 - ...DepositAccountReadPlatformServiceImpl.java | 19 +- ...WritePlatformServiceJpaRepositoryImpl.java | 39 +++- .../DepositsDropdownReadPlatformService.java | 2 + ...positsDropdownReadPlatformServiceImpl.java | 12 ++ .../savings/service/SavingsEnumerations.java | 11 +- .../V358__fixed_deposit_rollover_transfer.sql | 21 ++ 23 files changed, 597 insertions(+), 66 deletions(-) create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V358__fixed_deposit_rollover_transfer.sql 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 815c485f01a..dc065389c9a 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 void testFixedDepositWithQuarterlyCompoundingAndQuarterlyPosting_360_Days 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 ---------------------------------------"); @@ -2470,6 +2639,17 @@ private Integer applyForFixedDepositApplication(final String clientID, final Str .applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec); } + 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 --------------------------------"); 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 ded5afbd884..fa4c50b7d64 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 FixedDepositAccountHelper(final RequestSpecification requestSpec, final R 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 map = new HashMap<>(); @@ -127,6 +128,7 @@ public String build(final String clientId, final String productId, final String 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 @@ private String getDepositAmount() { } 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 7ca8d3a1e57..5008fdec67e 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 static JsonCommand fromJsonElement(final Long resourceId, final JsonEleme 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 JsonCommand(final Long resourceId, final JsonElement parsedCommand) { 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 aad6f344e10..2ae6edab527 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 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 8c0bfa4a3ed..9f6a9bb0c07 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 AccountAssociationsData mapRow(final ResultSet rs, @SuppressWarnings("unu } } + @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 29ccd73388a..fc7a2dc5147 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 static DepositAccountOnClosureType fromInt(final Integer closureTypeValue 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 boolean isTransferToSavings() { } 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 b667fcc0d51..44266f14dae 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 @@ private static Set recurringDepositProductResponseData() { interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, chargesParamName, chartsParamName, depositAmountParamName, depositPeriodParamName, depositPeriodFrequencyIdParamName, - savingsAccounts, expectedFirstDepositOnDateParamName, SavingsApiConstants.withHoldTaxParamName)); + savingsAccounts, expectedFirstDepositOnDateParamName, SavingsApiConstants.withHoldTaxParamName, + maturityInstructionIdParamName, transferToSavingsIdParamName)); public static final Set FIXED_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS = fixedDepositAccountRequestData(); public static final Set 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 c8ffc35de78..fa67b79363d 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 @@ private FixedDepositAccountData populateTemplateAndAssociations(final Long accou Collection transactions = null; Collection charges = null; PortfolioAccountData linkedAccount = null; + PortfolioAccountData transferToSavingsAccount = null; final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); if (!associationParameters.isEmpty()) { @@ -272,6 +273,10 @@ private FixedDepositAccountData populateTemplateAndAssociations(final Long accou } } + 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 @@ private FixedDepositAccountData populateTemplateAndAssociations(final Long accou 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 b95c6c7cb13..cc0a1bf20d6 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.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.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.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 @@ private void validateDepositDetailsForSubmit(final JsonElement element, final Da } 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 @@ private void validateDepositDetailsForUpdate(final JsonElement element, final Da } } + 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 eb7a74dfc96..63fccc15441 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 preClosurePenalInterestOnTypeOptions; private Collection periodFrequencyTypeOptions; @@ -67,6 +69,7 @@ public class FixedDepositAccountData extends DepositAccountData { // for account close private Collection onAccountClosureOptions; private Collection paymentTypeOptions; + private final Collection maturityInstructionOptions; //import fields private transient Integer rowIndex; @@ -114,6 +117,7 @@ private FixedDepositAccountData(Long clientId,Long productId,Long fieldofficerId 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 @@ private FixedDepositAccountData(Long clientId,Long productId,Long fieldofficerId this.locale= locale; this.submittedOnDate = submittedOnDate; this.depositPeriodFrequencyId = depositPeriodFrequencyId; + this.maturityInstructionOptions = null; } public Integer getRowIndex() { @@ -136,11 +141,14 @@ public static FixedDepositAccountData instance(final DepositAccountData depositA 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 preClosurePenalInterestOnTypeOptions = null; final Collection periodFrequencyTypeOptions = null; + final Collection maturityInstructionOptions = null; final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue()); final Collection onAccountClosureOptions = null; @@ -166,7 +174,7 @@ public static FixedDepositAccountData instance(final DepositAccountData depositA 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 static FixedDepositAccountData withInterestChart(final FixedDepositAccoun 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 transactions, final Collection charges, - final PortfolioAccountData linkedAccount) { + final PortfolioAccountData linkedAccount, PortfolioAccountData transferToSavingsAccount ) { if (template == null) { template = account; @@ -214,7 +223,8 @@ public static FixedDepositAccountData associationsAndTemplate(final FixedDeposit 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 static FixedDepositAccountData withTemplateOptions(final FixedDepositAcco final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, final Collection transactions, final Collection charges, final Collection chargeOptions, final Collection preClosurePenalInterestOnTypeOptions, - final Collection periodFrequencyTypeOptions, final Collection savingsAccounts) { + final Collection periodFrequencyTypeOptions, final Collection savingsAccounts, + final Collection 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 static FixedDepositAccountData withTemplateOptions(final FixedDepositAcco 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 static FixedDepositAccountData withClientTemplate(final Long clientId, fi final EnumOptionData depositPeriodFrequency = null; final EnumOptionData onAccountClosure = null; final PortfolioAccountData linkedAccount = null; + final PortfolioAccountData transferToSavingsAccount = null; final Boolean transferInterestToSavings = null; final Collection preClosurePenalInterestOnTypeOptions = null; final Collection periodFrequencyTypeOptions = null; @@ -312,6 +325,8 @@ public static FixedDepositAccountData withClientTemplate(final Long clientId, fi final Collection onAccountClosureOptions = null; final Collection paymentTypeOptions = null; final Collection savingsAccountDatas = null; + final Collection 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 static FixedDepositAccountData withClientTemplate(final Long clientId, fi 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 static FixedDepositAccountData preClosureDetails(final Long accountId, Bi final PortfolioAccountData linkedAccount = null; final boolean withHoldTax = false; final TaxGroupData taxGroup = null; + final Collection 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 static FixedDepositAccountData preClosureDetails(final Long accountId, Bi 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 static FixedDepositAccountData withClosureTemplateDetails(final FixedDepo 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 transactions, final Collection productOptions, - final Collection fieldOfficerOptions, final Collection interestCompoundingPeriodTypeOptions, - final Collection interestPostingPeriodTypeOptions, - final Collection interestCalculationTypeOptions, - final Collection interestCalculationDaysInYearTypeOptions, - final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, - final Collection charges, final Collection chargeOptions, - final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate, - final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest, - final EnumOptionData preClosurePenalInterestOnType, final Collection 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 periodFrequencyTypeOptions, final EnumOptionData depositType, - final EnumOptionData onAccountClosure, final Collection onAccountClosureOptions, - final Collection paymentTypeOptions, final Collection 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 transactions, final Collection productOptions, + final Collection fieldOfficerOptions, final Collection interestCompoundingPeriodTypeOptions, + final Collection interestPostingPeriodTypeOptions, + final Collection interestCalculationTypeOptions, + final Collection interestCalculationDaysInYearTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection charges, final Collection chargeOptions, + final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate, + final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest, + final EnumOptionData preClosurePenalInterestOnType, final Collection 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 periodFrequencyTypeOptions, final EnumOptionData depositType, + final EnumOptionData onAccountClosure, final Collection onAccountClosureOptions, + final Collection paymentTypeOptions, final Collection savingsAccountDatas, + final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings, final boolean withHoldTax, + final TaxGroupData taxGroup, final Collection 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 @@ private FixedDepositAccountData(final Long id, final String accountNo, final Str this.depositPeriodFrequency = depositPeriodFrequency; this.onAccountClosure = onAccountClosure; this.linkedAccount = linkedAccount; + this.transferToSavingsAccount = transferToSavingsAccount; this.transferInterestToSavings = transferInterestToSavings; // template @@ -495,6 +516,8 @@ private FixedDepositAccountData(final Long id, final String accountNo, final Str this.onAccountClosureOptions = onAccountClosureOptions; this.paymentTypeOptions = paymentTypeOptions; this.savingsAccounts = savingsAccountDatas; + this.maturityInstructionOptions = maturityInstructionOptions; + this.transferToSavingsId = transferToSavingsId; } @Override @@ -521,4 +544,8 @@ public BigDecimal getActivationCharge() { 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 6591ec30a0d..283bfd5dfb5 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.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 SavingsAccount assembleFrom(final JsonCommand command, final AppUser subm 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 DepositAccountTermAndPreClosure assembleAccountTermAndPreClosure(final Js 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 08f602eec2c..40d4a5f5977 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.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 @@ SavingsAccountTransaction handleSavingDeposit(SavingsAccount account, DateTimeFo Long handleFDAccountClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, LocalDate tenantsTodayDate, Map changes); + @Transactional + Long handleFDAccountMaturityClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, + LocalDate tenantsTodayDate, DateTimeFormatter fmt, + LocalDate closedDate, Integer onAccountClosureId, + Long toSavingsId, String transferDescription, Map changes); + Long handleRDAccountClosure(RecurringDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, - LocalDate tenantsTodayDate, Map changes); + LocalDate tenantsTodayDate, Map changes); Long handleFDAccountPreMatureClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, LocalDate tenantsTodayDate, Map 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 e8b5d7f7f80..c9a5f97b158 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 @@ private boolean isAnyActivationChargesDue(final RecurringDepositAccount account) @Transactional @Override public Long handleFDAccountClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user, - final JsonCommand command, final LocalDate tenantsTodayDate, final Map changes) { + final JsonCommand command, final LocalDate tenantsTodayDate, final Map changes) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); @@ -261,6 +261,85 @@ public Long handleFDAccountClosure(final FixedDepositAccount account, final Paym return savingsTransactionId; } + @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 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 existingTransactionIds = new HashSet<>(); + final Set 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, @@ -290,7 +369,12 @@ public Long handleRDAccountClosure(final RecurringDepositAccount account, final 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 3b833a55299..f13a1b4f6f2 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.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 @@ protected DepositAccountTermAndPreClosure() { 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 @@ private DepositAccountTermAndPreClosure(DepositPreClosureDetail preClosureDetail this.expectedFirstDepositOnDate = expectedFirstDepositOnDate == null ? null : expectedFirstDepositOnDate.toDate(); this.onAccountClosureType = (accountOnClosureType == null) ? null : accountOnClosureType.getValue(); this.transferInterestToLinkedAccount = transferInterest; + this.transferToSavingsAccountId = transferToSavingsId; } public Map update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) { @@ -158,6 +164,20 @@ public Map update(final JsonCommand command, final DataValidator 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 DepositAccountTermAndPreClosure copy(BigDecimal depositAmount) { 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 boolean isAfterExpectedFirstDepositDate(final LocalDate compareDate) { } 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 2c909ef1040..d5e23b9cf13 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 void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrent } } + + public LocalDate calculateMaturityDate() { final LocalDate startDate = accountSubmittedOrActivationDate(); @@ -489,6 +491,14 @@ public void close(final AppUser currentUser, final JsonCommand command, final Lo // 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 boolean isTransferToSavingsOnClosure() { 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 void loadLazyCollections() { 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 79d592d938c..670d53cd016 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 void loadLazyCollections() { 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 c52e67664d6..a1499c17d74 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 void modifyApplication(final JsonCommand command, final Map chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly); + final Collection maturityInstructionOptions = this.depositsDropdownReadPlatformService.maturityInstructionOptions(); + Collection fieldOfficerOptions = null; if (officeId != null) { @@ -405,7 +407,7 @@ public DepositAccountData retrieveTemplate(final DepositAccountType depositAccou } // 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 DepositAccountData retrieveTemplate(final DepositAccountType depositAccou 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 DepositAccountData retrieveTemplate(final DepositAccountType depositAccou final Collection transactions = null; final Collection charges = null; + final Collection maturityInstructionOptions = null; final boolean feeChargesOnly = true; final Collection chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly); @@ -462,7 +465,7 @@ public DepositAccountData retrieveTemplate(final DepositAccountType depositAccou 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 FixedDepositAccountMapper() { 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 FixedDepositAccountData mapRow(final ResultSet rs, @SuppressWarnings("unu .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 FixedDepositAccountData mapRow(final ResultSet rs, @SuppressWarnings("unu 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 c1c5fc56533..2e42c5ca031 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.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.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 DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurit 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 DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurit this.calendarInstanceRepository = calendarInstanceRepository; this.configurationDomainService = configurationDomainService; this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository; + this.fromApiJsonHelper = fromApiJsonHelper; } @Transactional @@ -798,14 +803,14 @@ public CommandProcessingResult closeFDAccount(final Long savingsId, final JsonCo this.depositAccountTransactionDataValidator.validateClosing(command, DepositAccountType.FIXED_DEPOSIT, isPreMatureClose); final Map 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 void updateMaturityDetails(Long depositAccountId, DepositAccountType 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 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 d186a69898a..07c041f74cb 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 retrievePreClosurePenalInterestOnTypeOptions(); + Collection maturityInstructionOptions(); + /* * Collection 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 70ce68fae99..7c172e09ac2 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 Collection retrievePreClosurePenalInterestOnTypeOptions() * .recurringDepositFrequencyType(SavingsPeriodFrequencyType.values()); } */ + + @Override + public Collection 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 b552cbd189c..b0b2644c19b 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 static EnumOptionData depositAccountOnClosureType(final DepositAccountOnC 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 static EnumOptionData onHoldTransactionType(final DepositAccountOnHoldTra } 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 00000000000..6bb8300d4ab --- /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