FINERACT-2152: Interest pause during one period and between two periods
This commit is contained in:
committed by
Adam Saghy
parent
a9d76ef3dd
commit
af37e07f31
@@ -88,6 +88,7 @@ import org.apache.fineract.client.services.LoanChargesApi;
|
|||||||
import org.apache.fineract.client.services.LoanCobCatchUpApi;
|
import org.apache.fineract.client.services.LoanCobCatchUpApi;
|
||||||
import org.apache.fineract.client.services.LoanCollateralApi;
|
import org.apache.fineract.client.services.LoanCollateralApi;
|
||||||
import org.apache.fineract.client.services.LoanDisbursementDetailsApi;
|
import org.apache.fineract.client.services.LoanDisbursementDetailsApi;
|
||||||
|
import org.apache.fineract.client.services.LoanInterestPauseApi;
|
||||||
import org.apache.fineract.client.services.LoanProductsApi;
|
import org.apache.fineract.client.services.LoanProductsApi;
|
||||||
import org.apache.fineract.client.services.LoanReschedulingApi;
|
import org.apache.fineract.client.services.LoanReschedulingApi;
|
||||||
import org.apache.fineract.client.services.LoanTransactionsApi;
|
import org.apache.fineract.client.services.LoanTransactionsApi;
|
||||||
@@ -290,6 +291,7 @@ public final class FineractClient {
|
|||||||
public final UserGeneratedDocumentsApi templates;
|
public final UserGeneratedDocumentsApi templates;
|
||||||
public final UsersApi users;
|
public final UsersApi users;
|
||||||
public final WorkingDaysApi workingDays;
|
public final WorkingDaysApi workingDays;
|
||||||
|
public final LoanInterestPauseApi loanInterestPauseApi;
|
||||||
|
|
||||||
public final ExternalAssetOwnersApi externalAssetOwners;
|
public final ExternalAssetOwnersApi externalAssetOwners;
|
||||||
public final ExternalAssetOwnerLoanProductAttributesApi externalAssetOwnerLoanProductAttributes;
|
public final ExternalAssetOwnerLoanProductAttributesApi externalAssetOwnerLoanProductAttributes;
|
||||||
@@ -415,6 +417,7 @@ public final class FineractClient {
|
|||||||
templates = retrofit.create(UserGeneratedDocumentsApi.class);
|
templates = retrofit.create(UserGeneratedDocumentsApi.class);
|
||||||
users = retrofit.create(UsersApi.class);
|
users = retrofit.create(UsersApi.class);
|
||||||
workingDays = retrofit.create(WorkingDaysApi.class);
|
workingDays = retrofit.create(WorkingDaysApi.class);
|
||||||
|
loanInterestPauseApi = retrofit.create(LoanInterestPauseApi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
|
|||||||
+6
@@ -40,6 +40,7 @@ import org.apache.fineract.client.services.JournalEntriesApi;
|
|||||||
import org.apache.fineract.client.services.LoanAccountLockApi;
|
import org.apache.fineract.client.services.LoanAccountLockApi;
|
||||||
import org.apache.fineract.client.services.LoanChargesApi;
|
import org.apache.fineract.client.services.LoanChargesApi;
|
||||||
import org.apache.fineract.client.services.LoanCobCatchUpApi;
|
import org.apache.fineract.client.services.LoanCobCatchUpApi;
|
||||||
|
import org.apache.fineract.client.services.LoanInterestPauseApi;
|
||||||
import org.apache.fineract.client.services.LoanProductsApi;
|
import org.apache.fineract.client.services.LoanProductsApi;
|
||||||
import org.apache.fineract.client.services.LoanTransactionsApi;
|
import org.apache.fineract.client.services.LoanTransactionsApi;
|
||||||
import org.apache.fineract.client.services.LoansApi;
|
import org.apache.fineract.client.services.LoansApi;
|
||||||
@@ -244,4 +245,9 @@ public class ApiConfiguration {
|
|||||||
public RescheduleLoansApi rescheduleLoansApi() {
|
public RescheduleLoansApi rescheduleLoansApi() {
|
||||||
return fineractClient.createService(RescheduleLoansApi.class);
|
return fineractClient.createService(RescheduleLoansApi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public LoanInterestPauseApi loanInterestPauseApi() {
|
||||||
|
return fineractClient.createService(LoanInterestPauseApi.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.test.stepdef.loan;
|
||||||
|
|
||||||
|
import io.cucumber.java.en.And;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.fineract.client.models.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.client.models.InterestPauseRequestDto;
|
||||||
|
import org.apache.fineract.client.models.PostLoansResponse;
|
||||||
|
import org.apache.fineract.client.services.LoanInterestPauseApi;
|
||||||
|
import org.apache.fineract.test.helper.ErrorHelper;
|
||||||
|
import org.apache.fineract.test.stepdef.AbstractStepDef;
|
||||||
|
import org.apache.fineract.test.support.TestContextKey;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class LoanInterestPauseStepDef extends AbstractStepDef {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LoanInterestPauseApi loanInterestPauseApi;
|
||||||
|
|
||||||
|
@And("Customer creates interest pause with start date {string} and end date {string}")
|
||||||
|
public void createInterestPause(final String startDate, final String endDate) throws IOException {
|
||||||
|
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||||
|
assert loanResponse.body() != null;
|
||||||
|
final long loanId = loanResponse.body().getLoanId();
|
||||||
|
|
||||||
|
final InterestPauseRequestDto request = new InterestPauseRequestDto()//
|
||||||
|
.startDate(startDate)//
|
||||||
|
.endDate(endDate)//
|
||||||
|
.dateFormat("dd MMMM yyyy")//
|
||||||
|
.locale("en");//
|
||||||
|
|
||||||
|
final Response<CommandProcessingResult> createResponse = loanInterestPauseApi.createInterestPause(loanId, request).execute();
|
||||||
|
ErrorHelper.checkSuccessfulApiCall(createResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
@InterestPauseFeature
|
||||||
|
Feature: Loan interest pause on repayment schedule
|
||||||
|
|
||||||
|
Scenario: S1 - pause calculation within same period, interestRecalculation = true
|
||||||
|
When Admin sets the business date to "1 January 2024"
|
||||||
|
And Admin creates a client with random data
|
||||||
|
And Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | | | | 0.0 |
|
||||||
|
| 1 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||||
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 100 | 2.05 | 0 | 0 | 102.05 | 0 | 0 | 0 | 102.05 |
|
||||||
|
And Admin successfully approves the loan on "1 January 2024" with "100" amount and expected disbursement date on "1 January 2024"
|
||||||
|
And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||||
|
When Admin sets the business date to "1 February 2024"
|
||||||
|
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 EUR transaction amount
|
||||||
|
And Customer creates interest pause with start date "05 February 2024" and end date "10 February 2024"
|
||||||
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 29 | 01 March 2024 | | 66.95 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 3 | 31 | 01 April 2024 | | 50.33 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 4 | 30 | 01 May 2024 | | 33.61 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.8 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.8 | 0.1 | 0.0 | 0.0 | 16.9 | 0.0 | 0.0 | 0.0 | 16.9 |
|
||||||
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 100 | 1.95 | 0 | 0 | 101.95 | 17.01 | 0 | 0 | 84.94 |
|
||||||
|
|
||||||
|
Scenario: S2 - pause calculation between two periods, interestRecalculation = true
|
||||||
|
When Admin sets the business date to "1 January 2024"
|
||||||
|
And Admin creates a client with random data
|
||||||
|
And Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | | | | 0.0 |
|
||||||
|
| 1 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||||
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 100 | 2.05 | 0 | 0 | 102.05 | 0 | 0 | 0 | 102.05 |
|
||||||
|
And Admin successfully approves the loan on "1 January 2024" with "100" amount and expected disbursement date on "1 January 2024"
|
||||||
|
And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||||
|
When Admin sets the business date to "1 February 2024"
|
||||||
|
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 EUR transaction amount
|
||||||
|
And Customer creates interest pause with start date "10 February 2024" and end date "10 March 2024"
|
||||||
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 29 | 01 March 2024 | | 66.69 | 16.88 | 0.13 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 3 | 31 | 01 April 2024 | | 49.96 | 16.73 | 0.28 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 4 | 30 | 01 May 2024 | | 33.24 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.42 | 16.82 | 0.19 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.42 | 0.1 | 0.0 | 0.0 | 16.52 | 0.0 | 0.0 | 0.0 | 16.52 |
|
||||||
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 100 | 1.57 | 0 | 0 | 101.57 | 17.01 | 0 | 0 | 84.56 |
|
||||||
+19
-11
@@ -22,6 +22,7 @@ import static org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.ACTIVE
|
|||||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType.INTEREST_PAUSE;
|
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType.INTEREST_PAUSE;
|
||||||
import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
|
import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
@@ -43,6 +44,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@@ -51,24 +53,26 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
|||||||
|
|
||||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||||
|
private final LoanAssembler loanAssembler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult createInterestPause(ExternalId loanExternalId, String startDateString, String endDateString,
|
public CommandProcessingResult createInterestPause(final ExternalId loanExternalId, final String startDateString,
|
||||||
String dateFormat, String locale) {
|
final String endDateString, final String dateFormat, final String locale) {
|
||||||
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
|
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
|
||||||
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
|
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
|
||||||
|
final Loan loan = loanAssembler.assembleFrom(loanExternalId, false);
|
||||||
|
|
||||||
return processInterestPause(loanRepositoryWrapper.findOneWithNotFoundDetection(loanExternalId), startDate, endDate, dateFormat,
|
return processInterestPause(loan, startDate, endDate, dateFormat, locale);
|
||||||
locale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult createInterestPause(Long loanId, String startDateString, String endDateString, String dateFormat,
|
public CommandProcessingResult createInterestPause(final Long loanId, final String startDateString, final String endDateString,
|
||||||
String locale) {
|
final String dateFormat, final String locale) {
|
||||||
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
|
final LocalDate startDate = parseDate(startDateString, dateFormat, locale);
|
||||||
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
|
final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
|
||||||
|
final Loan loan = loanAssembler.assembleFrom(loanId, false);
|
||||||
|
|
||||||
return processInterestPause(loanRepositoryWrapper.findOneWithNotFoundDetection(loanId), startDate, endDate, dateFormat, locale);
|
return processInterestPause(loan, startDate, endDate, dateFormat, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -131,14 +135,18 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
|||||||
.with(Map.of("startDate", startDate.toString(), "endDate", endDate.toString())).build();
|
.with(Map.of("startDate", startDate.toString(), "endDate", endDate.toString())).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CommandProcessingResult processInterestPause(Loan loan, LocalDate startDate, LocalDate endDate, String dateFormat,
|
private CommandProcessingResult processInterestPause(final Loan loan, final LocalDate startDate, final LocalDate endDate,
|
||||||
String locale) {
|
String dateFormat, String locale) {
|
||||||
validateActiveLoan(loan);
|
validateActiveLoan(loan);
|
||||||
validateInterestPauseDates(loan, startDate, endDate, dateFormat, locale, null);
|
validateInterestPauseDates(loan, startDate, endDate, dateFormat, locale, null);
|
||||||
|
|
||||||
LoanTermVariations variation = new LoanTermVariations(INTEREST_PAUSE.getValue(), startDate, null, endDate, false, loan);
|
final LoanTermVariations variation = new LoanTermVariations(INTEREST_PAUSE.getValue(), startDate, BigDecimal.ZERO, endDate, false,
|
||||||
|
loan);
|
||||||
|
|
||||||
LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
|
final LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
|
||||||
|
|
||||||
|
loan.getLoanTermVariations().add(savedVariation);
|
||||||
|
loan.reprocessTransactions();
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder().withEntityId(savedVariation.getId()).build();
|
return new CommandProcessingResultBuilder().withEntityId(savedVariation.getId()).build();
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-19
@@ -23,16 +23,23 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
import lombok.Getter;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
|
||||||
public class LoanTermVariationsDataWrapper {
|
public class LoanTermVariationsDataWrapper {
|
||||||
|
|
||||||
|
@Getter
|
||||||
private final List<LoanTermVariationsData> exceptionData;
|
private final List<LoanTermVariationsData> exceptionData;
|
||||||
private ListIterator<LoanTermVariationsData> iterator;
|
private ListIterator<LoanTermVariationsData> iterator;
|
||||||
|
@Getter
|
||||||
private final List<LoanTermVariationsData> interestRateChanges;
|
private final List<LoanTermVariationsData> interestRateChanges;
|
||||||
|
@Getter
|
||||||
private final List<LoanTermVariationsData> interestRateFromInstallment;
|
private final List<LoanTermVariationsData> interestRateFromInstallment;
|
||||||
|
@Getter
|
||||||
private final List<LoanTermVariationsData> dueDateVariation;
|
private final List<LoanTermVariationsData> dueDateVariation;
|
||||||
private ListIterator<LoanTermVariationsData> dueDateIterator;
|
private ListIterator<LoanTermVariationsData> dueDateIterator;
|
||||||
|
@Getter
|
||||||
|
private final List<LoanTermVariationsData> interestPauseVariations;
|
||||||
|
|
||||||
public LoanTermVariationsDataWrapper(final List<LoanTermVariationsData> exceptionData) {
|
public LoanTermVariationsDataWrapper(final List<LoanTermVariationsData> exceptionData) {
|
||||||
if (exceptionData == null) {
|
if (exceptionData == null) {
|
||||||
@@ -43,6 +50,7 @@ public class LoanTermVariationsDataWrapper {
|
|||||||
this.interestRateChanges = new ArrayList<>();
|
this.interestRateChanges = new ArrayList<>();
|
||||||
this.dueDateVariation = new ArrayList<>();
|
this.dueDateVariation = new ArrayList<>();
|
||||||
this.interestRateFromInstallment = new ArrayList<>();
|
this.interestRateFromInstallment = new ArrayList<>();
|
||||||
|
this.interestPauseVariations = new ArrayList<>();
|
||||||
deriveLoanTermVariations();
|
deriveLoanTermVariations();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,18 +88,6 @@ public class LoanTermVariationsDataWrapper {
|
|||||||
return this.dueDateIterator.previous();
|
return this.dueDateIterator.previous();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<LoanTermVariationsData> getInterestRateChanges() {
|
|
||||||
return this.interestRateChanges;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<LoanTermVariationsData> getDueDateVariation() {
|
|
||||||
return this.dueDateVariation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<LoanTermVariationsData> getExceptionData() {
|
|
||||||
return this.exceptionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExceptionData(final List<LoanTermVariationsData> exceptionData) {
|
public void setExceptionData(final List<LoanTermVariationsData> exceptionData) {
|
||||||
clearTerms();
|
clearTerms();
|
||||||
this.exceptionData.addAll(exceptionData);
|
this.exceptionData.addAll(exceptionData);
|
||||||
@@ -103,10 +99,7 @@ public class LoanTermVariationsDataWrapper {
|
|||||||
this.interestRateChanges.clear();
|
this.interestRateChanges.clear();
|
||||||
this.dueDateVariation.clear();
|
this.dueDateVariation.clear();
|
||||||
this.interestRateFromInstallment.clear();
|
this.interestRateFromInstallment.clear();
|
||||||
}
|
this.interestPauseVariations.clear();
|
||||||
|
|
||||||
public List<LoanTermVariationsData> getInterestRateFromInstallment() {
|
|
||||||
return this.interestRateFromInstallment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int adjustNumberOfRepayments() {
|
public int adjustNumberOfRepayments() {
|
||||||
@@ -133,12 +126,11 @@ public class LoanTermVariationsDataWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasExceptionVariation(final LocalDate date, ListIterator<LoanTermVariationsData> exceptionDataListIterator) {
|
public boolean hasExceptionVariation(final LocalDate date, ListIterator<LoanTermVariationsData> exceptionDataListIterator) {
|
||||||
ListIterator<LoanTermVariationsData> iterator = exceptionDataListIterator;
|
return hasNext(date, exceptionDataListIterator);
|
||||||
return hasNext(date, iterator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateLoanTermVariationsData(final List<LoanTermVariationsData> exceptionData) {
|
public void updateLoanTermVariationsData(final List<LoanTermVariationsData> exceptionData) {
|
||||||
if (this.exceptionData != null && exceptionData != null && exceptionData.size() > 0) {
|
if (this.exceptionData != null && exceptionData != null && !exceptionData.isEmpty()) {
|
||||||
this.exceptionData.addAll(exceptionData);
|
this.exceptionData.addAll(exceptionData);
|
||||||
deriveLoanTermVariations();
|
deriveLoanTermVariations();
|
||||||
}
|
}
|
||||||
@@ -153,12 +145,15 @@ public class LoanTermVariationsDataWrapper {
|
|||||||
this.dueDateVariation.add(loanTermVariationsData);
|
this.dueDateVariation.add(loanTermVariationsData);
|
||||||
} else if (loanTermVariationsData.getTermVariationType().isInterestRateFromInstallment()) {
|
} else if (loanTermVariationsData.getTermVariationType().isInterestRateFromInstallment()) {
|
||||||
this.interestRateFromInstallment.add(loanTermVariationsData);
|
this.interestRateFromInstallment.add(loanTermVariationsData);
|
||||||
|
} else if (loanTermVariationsData.getTermVariationType().isInterestPauseVariation()) {
|
||||||
|
this.interestPauseVariations.add(loanTermVariationsData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(this.dueDateVariation);
|
Collections.sort(this.dueDateVariation);
|
||||||
this.exceptionData.removeAll(this.interestRateChanges);
|
this.exceptionData.removeAll(this.interestRateChanges);
|
||||||
this.exceptionData.removeAll(this.dueDateVariation);
|
this.exceptionData.removeAll(this.dueDateVariation);
|
||||||
this.exceptionData.removeAll(this.interestRateFromInstallment);
|
this.exceptionData.removeAll(this.interestRateFromInstallment);
|
||||||
|
this.exceptionData.removeAll(this.interestPauseVariations);
|
||||||
this.iterator = this.exceptionData.listIterator();
|
this.iterator = this.exceptionData.listIterator();
|
||||||
this.dueDateIterator = this.dueDateVariation.listIterator();
|
this.dueDateIterator = this.dueDateVariation.listIterator();
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -98,6 +98,10 @@ public enum LoanTermVariationType {
|
|||||||
return this.value.equals(LoanTermVariationType.INTEREST_RATE.getValue());
|
return this.value.equals(LoanTermVariationType.INTEREST_RATE.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInterestPauseVariation() {
|
||||||
|
return this.value.equals(LoanTermVariationType.INTEREST_PAUSE.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isPrincipalAmountVariation() {
|
public boolean isPrincipalAmountVariation() {
|
||||||
return this.value.equals(LoanTermVariationType.PRINCIPAL_AMOUNT.getValue());
|
return this.value.equals(LoanTermVariationType.PRINCIPAL_AMOUNT.getValue());
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.organisation.staff.domain.Staff;
|
import org.apache.fineract.organisation.staff.domain.Staff;
|
||||||
import org.apache.fineract.portfolio.fund.domain.Fund;
|
import org.apache.fineract.portfolio.fund.domain.Fund;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
@@ -30,6 +31,12 @@ public interface LoanAssembler {
|
|||||||
|
|
||||||
Loan assembleFrom(Long accountId);
|
Loan assembleFrom(Long accountId);
|
||||||
|
|
||||||
|
Loan assembleFrom(Long accountId, boolean loadLazyCollections);
|
||||||
|
|
||||||
|
Loan assembleFrom(ExternalId externalId);
|
||||||
|
|
||||||
|
Loan assembleFrom(ExternalId externalId, boolean loadLazyCollections);
|
||||||
|
|
||||||
Loan assembleFrom(JsonCommand command);
|
Loan assembleFrom(JsonCommand command);
|
||||||
|
|
||||||
void setHelpers(Loan loanAccount);
|
void setHelpers(Loan loanAccount);
|
||||||
|
|||||||
+14
-4
@@ -261,7 +261,12 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
final LoanTermVariationsData interestRateChange, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
final LoanTermVariationsData interestRateChange, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||||
final LocalDate interestRateChangeSubmittedOnDate = interestRateChange.getTermVariationApplicableFrom();
|
final LocalDate interestRateChangeSubmittedOnDate = interestRateChange.getTermVariationApplicableFrom();
|
||||||
final BigDecimal newInterestRate = interestRateChange.getDecimalValue();
|
final BigDecimal newInterestRate = interestRateChange.getDecimalValue();
|
||||||
emiCalculator.changeInterestRate(scheduleModel, interestRateChangeSubmittedOnDate, newInterestRate);
|
if (interestRateChange.getTermVariationType().isInterestPauseVariation()) {
|
||||||
|
final LocalDate pauseEndDate = interestRateChange.getDateValue();
|
||||||
|
emiCalculator.applyInterestPause(scheduleModel, interestRateChangeSubmittedOnDate, pauseEndDate);
|
||||||
|
} else {
|
||||||
|
emiCalculator.changeInterestRate(scheduleModel, interestRateChangeSubmittedOnDate, newInterestRate);
|
||||||
|
}
|
||||||
processInterestRateChangeOnInstallments(scheduleModel, interestRateChangeSubmittedOnDate, installments);
|
processInterestRateChangeOnInstallments(scheduleModel, interestRateChangeSubmittedOnDate, installments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,9 +825,14 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
@NotNull
|
@NotNull
|
||||||
private List<ChangeOperation> createSortedChangeList(final LoanTermVariationsDataWrapper loanTermVariations,
|
private List<ChangeOperation> createSortedChangeList(final LoanTermVariationsDataWrapper loanTermVariations,
|
||||||
final List<LoanTransaction> loanTransactions, final Set<LoanCharge> charges) {
|
final List<LoanTransaction> loanTransactions, final Set<LoanCharge> charges) {
|
||||||
List<ChangeOperation> changeOperations = new ArrayList<>();
|
final List<ChangeOperation> changeOperations = new ArrayList<>();
|
||||||
if (loanTermVariations != null && !loanTermVariations.getInterestRateFromInstallment().isEmpty()) {
|
if (loanTermVariations != null) {
|
||||||
changeOperations.addAll(loanTermVariations.getInterestRateFromInstallment().stream().map(ChangeOperation::new).toList());
|
if (!loanTermVariations.getInterestPauseVariations().isEmpty()) {
|
||||||
|
changeOperations.addAll(loanTermVariations.getInterestPauseVariations().stream().map(ChangeOperation::new).toList());
|
||||||
|
}
|
||||||
|
if (!loanTermVariations.getInterestRateFromInstallment().isEmpty()) {
|
||||||
|
changeOperations.addAll(loanTermVariations.getInterestRateFromInstallment().stream().map(ChangeOperation::new).toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (charges != null) {
|
if (charges != null) {
|
||||||
changeOperations.addAll(charges.stream().map(ChangeOperation::new).toList());
|
changeOperations.addAll(charges.stream().map(ChangeOperation::new).toList());
|
||||||
|
|||||||
+9
-15
@@ -23,6 +23,7 @@ import java.math.BigDecimal;
|
|||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -34,6 +35,7 @@ import org.apache.fineract.organisation.monetary.domain.Money;
|
|||||||
@Getter
|
@Getter
|
||||||
@ToString(exclude = { "repaymentPeriod" })
|
@ToString(exclude = { "repaymentPeriod" })
|
||||||
@EqualsAndHashCode(exclude = { "repaymentPeriod" })
|
@EqualsAndHashCode(exclude = { "repaymentPeriod" })
|
||||||
|
@AllArgsConstructor
|
||||||
public class InterestPeriod implements Comparable<InterestPeriod> {
|
public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||||
|
|
||||||
private final RepaymentPeriod repaymentPeriod;
|
private final RepaymentPeriod repaymentPeriod;
|
||||||
@@ -49,25 +51,13 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
private Money balanceCorrectionAmount;
|
private Money balanceCorrectionAmount;
|
||||||
private Money outstandingLoanBalance;
|
private Money outstandingLoanBalance;
|
||||||
private final MathContext mc;
|
private final MathContext mc;
|
||||||
|
private final boolean isPaused;
|
||||||
public InterestPeriod(RepaymentPeriod repaymentPeriod, LocalDate fromDate, LocalDate dueDate, BigDecimal rateFactor,
|
|
||||||
BigDecimal rateFactorTillPeriodDueDate, Money disbursementAmount, Money balanceCorrectionAmount, Money outstandingLoanBalance,
|
|
||||||
MathContext mc) {
|
|
||||||
this.repaymentPeriod = repaymentPeriod;
|
|
||||||
this.fromDate = fromDate;
|
|
||||||
this.dueDate = dueDate;
|
|
||||||
this.rateFactor = rateFactor;
|
|
||||||
this.rateFactorTillPeriodDueDate = rateFactorTillPeriodDueDate;
|
|
||||||
this.disbursementAmount = disbursementAmount;
|
|
||||||
this.balanceCorrectionAmount = balanceCorrectionAmount;
|
|
||||||
this.outstandingLoanBalance = outstandingLoanBalance;
|
|
||||||
this.mc = mc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod interestPeriod) {
|
public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod interestPeriod) {
|
||||||
this(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(), interestPeriod.getRateFactor(),
|
this(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(), interestPeriod.getRateFactor(),
|
||||||
interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getDisbursementAmount(),
|
interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getDisbursementAmount(),
|
||||||
interestPeriod.getBalanceCorrectionAmount(), interestPeriod.getOutstandingLoanBalance(), interestPeriod.getMc());
|
interestPeriod.getBalanceCorrectionAmount(), interestPeriod.getOutstandingLoanBalance(), interestPeriod.getMc(),
|
||||||
|
interestPeriod.isPaused());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -84,6 +74,10 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Money getCalculatedDueInterest() {
|
public Money getCalculatedDueInterest() {
|
||||||
|
if (isPaused) {
|
||||||
|
return Money.zero(outstandingLoanBalance.getCurrencyData(), mc);
|
||||||
|
}
|
||||||
|
|
||||||
long lengthTillPeriodDueDate = getLengthTillPeriodDueDate();
|
long lengthTillPeriodDueDate = getLengthTillPeriodDueDate();
|
||||||
final BigDecimal interestDueTillRepaymentDueDate = lengthTillPeriodDueDate == 0 //
|
final BigDecimal interestDueTillRepaymentDueDate = lengthTillPeriodDueDate == 0 //
|
||||||
? BigDecimal.ZERO //
|
? BigDecimal.ZERO //
|
||||||
|
|||||||
+77
-15
@@ -160,6 +160,24 @@ public class ProgressiveLoanInterestScheduleModel {
|
|||||||
.findFirst();//
|
.findFirst();//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<RepaymentPeriod> updateInterestPeriodsForInterestPause(final LocalDate fromDate, final LocalDate endDate) {
|
||||||
|
if (fromDate == null || endDate == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<RepaymentPeriod> affectedPeriods = repaymentPeriods.stream()//
|
||||||
|
.filter(period -> isPeriodInRange(period, fromDate, endDate))//
|
||||||
|
.toList();
|
||||||
|
affectedPeriods.forEach(period -> insertInterestPausePeriods(period, fromDate, endDate));
|
||||||
|
|
||||||
|
return affectedPeriods.stream().findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPeriodInRange(final RepaymentPeriod repaymentPeriod, final LocalDate fromDate, final LocalDate endDate) {
|
||||||
|
return DateUtils.isDateInRangeFromExclusiveToInclusive(fromDate, repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate())
|
||||||
|
|| DateUtils.isDateInRangeFromExclusiveToInclusive(endDate, repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate());
|
||||||
|
}
|
||||||
|
|
||||||
Optional<RepaymentPeriod> findRepaymentPeriodForBalanceChange(final LocalDate balanceChangeDate) {
|
Optional<RepaymentPeriod> findRepaymentPeriodForBalanceChange(final LocalDate balanceChangeDate) {
|
||||||
if (balanceChangeDate == null) {
|
if (balanceChangeDate == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@@ -192,7 +210,8 @@ public class ProgressiveLoanInterestScheduleModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<InterestPeriod> findInterestPeriodForBalanceChange(final RepaymentPeriod repaymentPeriod, final LocalDate balanceChangeDate) {
|
private Optional<InterestPeriod> findInterestPeriodForBalanceChange(final RepaymentPeriod repaymentPeriod,
|
||||||
|
final LocalDate balanceChangeDate) {
|
||||||
if (repaymentPeriod == null || balanceChangeDate == null) {
|
if (repaymentPeriod == null || balanceChangeDate == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
@@ -203,26 +222,63 @@ public class ProgressiveLoanInterestScheduleModel {
|
|||||||
|
|
||||||
void insertInterestPeriod(final RepaymentPeriod repaymentPeriod, final LocalDate balanceChangeDate, final Money disbursedAmount,
|
void insertInterestPeriod(final RepaymentPeriod repaymentPeriod, final LocalDate balanceChangeDate, final Money disbursedAmount,
|
||||||
final Money correctionAmount) {
|
final Money correctionAmount) {
|
||||||
final InterestPeriod previousInterestPeriod;
|
final InterestPeriod previousInterestPeriod = findPreviousInterestPeriod(repaymentPeriod, balanceChangeDate);
|
||||||
if (balanceChangeDate.isAfter(repaymentPeriod.getFromDate())) {
|
final LocalDate originalDueDate = previousInterestPeriod.getDueDate();
|
||||||
previousInterestPeriod = repaymentPeriod.getInterestPeriods().get(repaymentPeriod.getInterestPeriods().size() - 1);//
|
final LocalDate newDueDate = calculateNewDueDate(previousInterestPeriod, balanceChangeDate);
|
||||||
} else {
|
|
||||||
previousInterestPeriod = repaymentPeriod.getInterestPeriods().stream()
|
|
||||||
.filter(ip -> balanceChangeDate.isAfter(ip.getFromDate()) && !balanceChangeDate.isAfter(ip.getDueDate()))//
|
|
||||||
.reduce((first, second) -> second)//
|
|
||||||
.orElse(repaymentPeriod.getInterestPeriods().get(0));
|
|
||||||
}
|
|
||||||
LocalDate originalDueDate = previousInterestPeriod.getDueDate();
|
|
||||||
LocalDate newDueDate = balanceChangeDate.isBefore(previousInterestPeriod.getFromDate()) ? previousInterestPeriod.getFromDate()
|
|
||||||
: balanceChangeDate.isAfter(previousInterestPeriod.getDueDate()) ? previousInterestPeriod.getDueDate() : balanceChangeDate;
|
|
||||||
previousInterestPeriod.setDueDate(newDueDate);
|
previousInterestPeriod.setDueDate(newDueDate);
|
||||||
previousInterestPeriod.addDisbursementAmount(disbursedAmount);
|
previousInterestPeriod.addDisbursementAmount(disbursedAmount);
|
||||||
previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount);
|
previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount);
|
||||||
final InterestPeriod interestPeriod = new InterestPeriod(repaymentPeriod, previousInterestPeriod.getDueDate(), originalDueDate,
|
|
||||||
BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, mc);
|
final InterestPeriod interestPeriod = new InterestPeriod(repaymentPeriod, newDueDate, originalDueDate, BigDecimal.ZERO,
|
||||||
|
BigDecimal.ZERO, zero, zero, zero, mc, false);
|
||||||
repaymentPeriod.getInterestPeriods().add(interestPeriod);
|
repaymentPeriod.getInterestPeriods().add(interestPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void insertInterestPausePeriods(final RepaymentPeriod repaymentPeriod, final LocalDate fromDate, final LocalDate endDate) {
|
||||||
|
final InterestPeriod previousInterestPeriod = findPreviousInterestPeriod(repaymentPeriod, fromDate);
|
||||||
|
final LocalDate originalFromDate = previousInterestPeriod.getFromDate();
|
||||||
|
final LocalDate originalDueDate = previousInterestPeriod.getDueDate();
|
||||||
|
final LocalDate newDueDate = calculateNewDueDate(previousInterestPeriod, fromDate.minusDays(1));
|
||||||
|
|
||||||
|
if (fromDate.isAfter(originalFromDate) && endDate.isBefore(originalDueDate)) {
|
||||||
|
previousInterestPeriod.setDueDate(newDueDate);
|
||||||
|
final InterestPeriod interestPausePeriod = new InterestPeriod(repaymentPeriod, newDueDate, endDate, BigDecimal.ZERO,
|
||||||
|
BigDecimal.ZERO, zero, zero, zero, mc, true);
|
||||||
|
repaymentPeriod.getInterestPeriods().add(interestPausePeriod);
|
||||||
|
final InterestPeriod interestAfterPausePeriod = new InterestPeriod(repaymentPeriod, endDate, originalDueDate, BigDecimal.ZERO,
|
||||||
|
BigDecimal.ZERO, zero, zero, zero, mc, false);
|
||||||
|
repaymentPeriod.getInterestPeriods().add(interestAfterPausePeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromDate.isAfter(originalFromDate) && endDate.isAfter(originalDueDate)) {
|
||||||
|
previousInterestPeriod.setDueDate(newDueDate);
|
||||||
|
final InterestPeriod interestPausePeriod = new InterestPeriod(repaymentPeriod, newDueDate, originalDueDate, BigDecimal.ZERO,
|
||||||
|
BigDecimal.ZERO, zero, zero, zero, mc, true);
|
||||||
|
repaymentPeriod.getInterestPeriods().add(interestPausePeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromDate.isBefore(originalFromDate) && endDate.isBefore(originalDueDate)) {
|
||||||
|
repaymentPeriod.getInterestPeriods().clear();
|
||||||
|
final InterestPeriod interestPausePeriod = new InterestPeriod(repaymentPeriod, newDueDate, originalDueDate, BigDecimal.ZERO,
|
||||||
|
BigDecimal.ZERO, zero, zero, zero, mc, true);
|
||||||
|
repaymentPeriod.getInterestPeriods().add(interestPausePeriod);
|
||||||
|
InterestPeriod interestAfterPausePeriod = new InterestPeriod(repaymentPeriod, endDate, originalDueDate, BigDecimal.ZERO,
|
||||||
|
BigDecimal.ZERO, zero, zero, zero, mc, false);
|
||||||
|
repaymentPeriod.getInterestPeriods().add(interestAfterPausePeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InterestPeriod findPreviousInterestPeriod(final RepaymentPeriod repaymentPeriod, final LocalDate date) {
|
||||||
|
if (date.isAfter(repaymentPeriod.getFromDate())) {
|
||||||
|
return repaymentPeriod.getInterestPeriods().get(repaymentPeriod.getInterestPeriods().size() - 1);
|
||||||
|
} else {
|
||||||
|
return repaymentPeriod.getInterestPeriods().stream()
|
||||||
|
.filter(ip -> date.isAfter(ip.getFromDate()) && !date.isAfter(ip.getDueDate())).reduce((first, second) -> second)
|
||||||
|
.orElse(repaymentPeriod.getInterestPeriods().get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Money getTotalDueInterest() {
|
public Money getTotalDueInterest() {
|
||||||
return repaymentPeriods().stream().flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest))
|
return repaymentPeriods().stream().flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest))
|
||||||
.reduce(zero(), Money::plus);
|
.reduce(zero(), Money::plus);
|
||||||
@@ -282,4 +338,10 @@ public class ProgressiveLoanInterestScheduleModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LocalDate calculateNewDueDate(final InterestPeriod previousInterestPeriod, final LocalDate date) {
|
||||||
|
return date.isBefore(previousInterestPeriod.getFromDate()) ? previousInterestPeriod.getFromDate()
|
||||||
|
: date.isAfter(previousInterestPeriod.getDueDate()) ? previousInterestPeriod.getDueDate() : date;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ public class RepaymentPeriod {
|
|||||||
this.interestPeriods = new ArrayList<>();
|
this.interestPeriods = new ArrayList<>();
|
||||||
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
||||||
getInterestPeriods().add(new InterestPeriod(this, getFromDate(), getDueDate(), BigDecimal.ZERO, BigDecimal.ZERO, getZero(mc),
|
getInterestPeriods().add(new InterestPeriod(this, getFromDate(), getDueDate(), BigDecimal.ZERO, BigDecimal.ZERO, getZero(mc),
|
||||||
getZero(mc), getZero(mc), mc));
|
getZero(mc), getZero(mc), mc, false));
|
||||||
this.paidInterest = getZero(mc);
|
this.paidInterest = getZero(mc);
|
||||||
this.paidPrincipal = getZero(mc);
|
this.paidPrincipal = getZero(mc);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-9
@@ -209,16 +209,22 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
|||||||
|
|
||||||
private void applyInterestRateChangesOnPeriod(final LoanApplicationTerms loanApplicationTerms,
|
private void applyInterestRateChangesOnPeriod(final LoanApplicationTerms loanApplicationTerms,
|
||||||
final LoanScheduleModelRepaymentPeriod repaymentPeriod, final ProgressiveLoanInterestScheduleModel interestScheduleModel) {
|
final LoanScheduleModelRepaymentPeriod repaymentPeriod, final ProgressiveLoanInterestScheduleModel interestScheduleModel) {
|
||||||
if (loanApplicationTerms.getLoanTermVariations() != null) {
|
if (loanApplicationTerms.getLoanTermVariations() == null) {
|
||||||
for (var interestRateChange : loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment()) {
|
return;
|
||||||
final LocalDate interestRateSubmittedOnDate = interestRateChange.getTermVariationApplicableFrom();
|
|
||||||
final BigDecimal newInterestRate = interestRateChange.getDecimalValue();
|
|
||||||
if (interestRateSubmittedOnDate.isAfter(repaymentPeriod.getFromDate())
|
|
||||||
&& !interestRateSubmittedOnDate.isAfter(repaymentPeriod.getDueDate())) {
|
|
||||||
emiCalculator.changeInterestRate(interestScheduleModel, interestRateSubmittedOnDate, newInterestRate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment().stream()
|
||||||
|
.filter(change -> isDateWithinPeriod(change.getTermVariationApplicableFrom(), repaymentPeriod))
|
||||||
|
.forEach(change -> emiCalculator.changeInterestRate(interestScheduleModel, change.getTermVariationApplicableFrom(),
|
||||||
|
change.getDecimalValue()));
|
||||||
|
|
||||||
|
loanApplicationTerms.getLoanTermVariations().getInterestPauseVariations().stream()
|
||||||
|
.filter(pause -> isDateWithinPeriod(pause.getTermVariationApplicableFrom(), repaymentPeriod)).forEach(pause -> emiCalculator
|
||||||
|
.applyInterestPause(interestScheduleModel, pause.getTermVariationApplicableFrom(), pause.getDateValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDateWithinPeriod(final LocalDate date, final LoanScheduleModelRepaymentPeriod period) {
|
||||||
|
return date.isAfter(period.getFromDate()) && !date.isAfter(period.getDueDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareDisbursementsOnLoanApplicationTerms(final LoanApplicationTerms loanApplicationTerms) {
|
private void prepareDisbursementsOnLoanApplicationTerms(final LoanApplicationTerms loanApplicationTerms) {
|
||||||
|
|||||||
+2
@@ -77,4 +77,6 @@ public interface EMICalculator {
|
|||||||
OutstandingDetails getOutstandingAmountsTillDate(ProgressiveLoanInterestScheduleModel model, LocalDate targetDate);
|
OutstandingDetails getOutstandingAmountsTillDate(ProgressiveLoanInterestScheduleModel model, LocalDate targetDate);
|
||||||
|
|
||||||
Money getSumOfDueInterestsOnDate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate subjectDate);
|
Money getSumOfDueInterestsOnDate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate subjectDate);
|
||||||
|
|
||||||
|
void applyInterestPause(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate fromDate, LocalDate endDate);
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -792,6 +792,20 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
.reduce(scheduleModel.zero(), Money::add); //
|
.reduce(scheduleModel.zero(), Money::add); //
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyInterestPause(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate fromDate,
|
||||||
|
final LocalDate endDate) {
|
||||||
|
scheduleModel.updateInterestPeriodsForInterestPause(fromDate, endDate)
|
||||||
|
.ifPresent(repaymentPeriod -> calculateRateFactorsForInterestPause(scheduleModel, repaymentPeriod.getFromDate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateRateFactorsForInterestPause(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate startDate) {
|
||||||
|
final List<RepaymentPeriod> relatedRepaymentPeriods = scheduleModel.getRelatedRepaymentPeriods(startDate);
|
||||||
|
calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
|
||||||
|
calculateOutstandingBalance(scheduleModel);
|
||||||
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
||||||
|
}
|
||||||
|
|
||||||
private long getUncountablePeriods(final List<RepaymentPeriod> relatedRepaymentPeriods, final Money originalEmi) {
|
private long getUncountablePeriods(final List<RepaymentPeriod> relatedRepaymentPeriods, final Money originalEmi) {
|
||||||
return relatedRepaymentPeriods.stream() //
|
return relatedRepaymentPeriods.stream() //
|
||||||
.filter(repaymentPeriod -> originalEmi.isLessThan(repaymentPeriod.getTotalPaidAmount())) //
|
.filter(repaymentPeriod -> originalEmi.isLessThan(repaymentPeriod.getTotalPaidAmount())) //
|
||||||
|
|||||||
+76
@@ -1342,6 +1342,82 @@ class ProgressiveEMICalculatorTest {
|
|||||||
checkDailyInterest(interestModel, dueDate, startDay, 31, 0.38, 9.03);
|
checkDailyInterest(interestModel, dueDate, startDay, 31, 0.38, 9.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_singleInterestPauseAmt100_dayInYears360_daysInMonth30_repayEvery1Month() {
|
||||||
|
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||||
|
repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1)),
|
||||||
|
repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1)),
|
||||||
|
repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1)),
|
||||||
|
repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)),
|
||||||
|
repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1)),
|
||||||
|
repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1)));
|
||||||
|
|
||||||
|
final BigDecimal interestRate = BigDecimal.valueOf(7.0);
|
||||||
|
final Integer installmentAmountInMultiplesOf = null;
|
||||||
|
|
||||||
|
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
|
||||||
|
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
|
||||||
|
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||||
|
|
||||||
|
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||||
|
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||||
|
|
||||||
|
final Money disbursedAmount = toMoney(100.0);
|
||||||
|
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||||
|
emiCalculator.applyInterestPause(interestSchedule, LocalDate.of(2024, 2, 5), LocalDate.of(2024, 2, 10));
|
||||||
|
|
||||||
|
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||||
|
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.58, 16.43, 83.57);
|
||||||
|
checkPeriod(interestSchedule, 1, 0, 17.01, 0.000603448276, 0.05, 0.39, 16.62, 66.95);
|
||||||
|
checkPeriod(interestSchedule, 1, 1, 17.01, 0.001206896552, 0.0, 0.39, 16.62, 66.95);
|
||||||
|
checkPeriod(interestSchedule, 1, 2, 17.01, 0.004022988506, 0.34, 0.39, 16.62, 66.95);
|
||||||
|
checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.39, 0.39, 16.62, 50.33);
|
||||||
|
checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.29, 0.29, 16.72, 33.61);
|
||||||
|
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.2, 0.2, 16.81, 16.8);
|
||||||
|
checkPeriod(interestSchedule, 5, 0, 16.9, 0.005833333333, 0.1, 0.1, 16.8, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_interestPauseBetweenTwoPeriodsAmt100_dayInYears360_daysInMonth30_repayEvery1Month() {
|
||||||
|
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||||
|
repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1)),
|
||||||
|
repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1)),
|
||||||
|
repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1)),
|
||||||
|
repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)),
|
||||||
|
repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1)),
|
||||||
|
repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1)));
|
||||||
|
|
||||||
|
final BigDecimal interestRate = BigDecimal.valueOf(7.0);
|
||||||
|
final Integer installmentAmountInMultiplesOf = null;
|
||||||
|
|
||||||
|
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
|
||||||
|
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
|
||||||
|
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||||
|
|
||||||
|
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||||
|
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||||
|
|
||||||
|
final Money disbursedAmount = toMoney(100.0);
|
||||||
|
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||||
|
emiCalculator.applyInterestPause(interestSchedule, LocalDate.of(2024, 2, 10), LocalDate.of(2024, 3, 10));
|
||||||
|
|
||||||
|
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||||
|
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.58, 16.43, 83.57);
|
||||||
|
checkPeriod(interestSchedule, 1, 0, 17.01, 0.001609195402, 0.13, 0.13, 16.88, 66.69);
|
||||||
|
checkPeriod(interestSchedule, 1, 1, 17.01, 0.004224137931, 0.0, 0.13, 16.88, 66.69);
|
||||||
|
checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.0, 0.28, 16.73, 49.96);
|
||||||
|
checkPeriod(interestSchedule, 2, 1, 17.01, 0.004139784946, 0.28, 0.28, 16.73, 49.96);
|
||||||
|
checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.29, 0.29, 16.72, 33.24);
|
||||||
|
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.19, 0.19, 16.82, 16.42);
|
||||||
|
checkPeriod(interestSchedule, 5, 0, 16.52, 0.005833333333, 0.1, 0.1, 16.42, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_reschedule_disbursedAmt100_dayInYears360_daysInMonth30_repayEvery1Month() {
|
public void test_reschedule_disbursedAmt100_dayInYears360_daysInMonth30_repayEvery1Month() {
|
||||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||||
|
|||||||
+24
-3
@@ -139,15 +139,36 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loan assembleFrom(final Long accountId) {
|
public Loan assembleFrom(final Long accountId) {
|
||||||
final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true);
|
return assembleFrom(accountId, true);
|
||||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine, this.loanRepaymentScheduleTransactionProcessorFactory);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loan assembleFrom(final Long accountId, final boolean loadLazyCollections) {
|
||||||
|
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(accountId, loadLazyCollections);
|
||||||
|
setHelpers(loanAccount);
|
||||||
|
|
||||||
|
return loanAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loan assembleFrom(final ExternalId externalId) {
|
||||||
|
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(externalId, true);
|
||||||
|
setHelpers(loanAccount);
|
||||||
|
|
||||||
|
return loanAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loan assembleFrom(final ExternalId externalId, final boolean loadLazyCollections) {
|
||||||
|
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(externalId, loadLazyCollections);
|
||||||
|
setHelpers(loanAccount);
|
||||||
|
|
||||||
return loanAccount;
|
return loanAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHelpers(final Loan loanAccount) {
|
public void setHelpers(final Loan loanAccount) {
|
||||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine, this.loanRepaymentScheduleTransactionProcessorFactory);
|
loanAccount.setHelpers(defaultLoanLifecycleStateMachine, loanRepaymentScheduleTransactionProcessorFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+2
-2
@@ -471,7 +471,7 @@ public class LoanAccountConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
|
@ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
|
||||||
public InterestPauseWritePlatformService interestPauseWritePlatformService(LoanTermVariationsRepository loanTermVariationsRepository,
|
public InterestPauseWritePlatformService interestPauseWritePlatformService(LoanTermVariationsRepository loanTermVariationsRepository,
|
||||||
LoanRepositoryWrapper loanRepositoryWrapper) {
|
LoanRepositoryWrapper loanRepositoryWrapper, LoanAssembler loanAssembler) {
|
||||||
return new InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, loanRepositoryWrapper);
|
return new InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, loanRepositoryWrapper, loanAssembler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user