Skip to content

Commit efc265f

Browse files
committed
FINERACT-2421: fix loan accrual posts after loan close
1 parent 8a61e30 commit efc265f

File tree

11 files changed

+909
-21
lines changed

11 files changed

+909
-21
lines changed

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,7 @@ periodNumberTemp, mc, mergeVariationsToMap(loanApplicationTerms, params), params
14621462
}
14631463
}
14641464

1465-
if (!outstanding.isZero()) {
1465+
if (!outstanding.isZero() || (params.getScheduleTillDate() != null && !params.getLatePaymentMap().isEmpty())) {
14661466
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
14671467
getPaymentPeriodsInOneYearCalculator(), interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(),
14681468
totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding, loanApplicationTerms,
@@ -1499,7 +1499,8 @@ periodNumberTemp, mc, mergeVariationsToMap(loanApplicationTerms, params), params
14991499
}
15001500
}
15011501
params.setPeriodStartDate(params.getActualRepaymentDate());
1502-
} while (DateUtils.isBefore(params.getActualRepaymentDate(), currentDate) && !outstanding.isZero());
1502+
} while (DateUtils.isBefore(params.getActualRepaymentDate(), currentDate)
1503+
&& (!outstanding.isZero() || (params.getScheduleTillDate() != null && !params.getLatePaymentMap().isEmpty())));
15031504

15041505
if (totalInterest.isGreaterThanZero()) {
15051506
LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(params.getInstalmentNumber(),

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -487,14 +487,19 @@ private void addInterestAccrual(@NonNull final Loan loan, @NonNull final LocalDa
487487
final AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber());
488488
final MonetaryCurrency currency = accrualPeriods.getCurrency();
489489
Money interest = null;
490-
final boolean isPastPeriod = isAfterPeriod(tillDate, installment);
491-
final boolean isInPeriod = isInPeriod(tillDate, installment, false);
490+
LocalDate effectiveDate = tillDate;
491+
if (loan.isClosed() && loan.getClosedOnDate() != null && DateUtils.isBefore(loan.getClosedOnDate(), tillDate)) {
492+
effectiveDate = loan.getClosedOnDate();
493+
} else if (loanBalanceService.isOverPaid(loan) && loan.getOverpaidOnDate() != null
494+
&& DateUtils.isBefore(loan.getOverpaidOnDate(), tillDate)) {
495+
effectiveDate = loan.getOverpaidOnDate();
496+
}
497+
final boolean isPastPeriod = isAfterPeriod(effectiveDate, installment);
498+
final boolean isInPeriod = isInPeriod(effectiveDate, installment, false);
492499
if (isPastPeriod || loan.isClosed() || loanBalanceService.isOverPaid(loan)) {
493500
interest = installment.getInterestCharged(currency).minus(installment.getCreditedInterest());
494-
} else {
495-
if (isInPeriod) { // first period first day is not accrued
496-
interest = scheduleGenerator.getPeriodInterestTillDate(installment, tillDate);
497-
}
501+
} else if (isInPeriod) {
502+
interest = scheduleGenerator.getPeriodInterestTillDate(installment, effectiveDate);
498503
}
499504
period.setInterestAmount(interest);
500505
Money accruable = null;

integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/FeignLoanTestBase.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.fineract.client.feign.FineractFeignClient;
2424
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
2525
import org.apache.fineract.client.models.GetLoansLoanIdStatus;
26+
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse;
2627
import org.apache.fineract.client.models.PostLoanProductsRequest;
2728
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
2829
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
@@ -97,6 +98,10 @@ protected Long createClient() {
9798
return clientHelper.createClient();
9899
}
99100

101+
protected Long createClient(String activationDate) {
102+
return clientHelper.createClient(activationDate);
103+
}
104+
100105
protected Long createLoanProduct(PostLoanProductsRequest request) {
101106
return loanHelper.createLoanProduct(request);
102107
}
@@ -219,4 +224,12 @@ protected PostLoansLoanIdTransactionsRequest waiveInterest(double amount, String
219224
protected PostLoansLoanIdTransactionsRequest chargeOff(String date) {
220225
return LoanRequestBuilders.chargeOff(date);
221226
}
227+
228+
protected void executeInlineCOB(Long loanId) {
229+
transactionHelper.executeInlineCOB(loanId);
230+
}
231+
232+
protected GetLoansLoanIdTransactionsTemplateResponse getPrepaymentAmount(Long loanId, String transactionDate, String dateFormat) {
233+
return transactionHelper.getPrepaymentAmount(loanId, transactionDate, dateFormat);
234+
}
222235
}

integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignBusinessDateHelper.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@
2828

2929
public class FeignBusinessDateHelper {
3030

31+
private static final String ENABLE_BUSINESS_DATE = "enable-business-date";
32+
3133
private final FineractFeignClient fineractClient;
34+
private final FeignGlobalConfigurationHelper configHelper;
3235

3336
public FeignBusinessDateHelper(FineractFeignClient fineractClient) {
3437
this.fineractClient = fineractClient;
38+
this.configHelper = new FeignGlobalConfigurationHelper(fineractClient);
3539
}
3640

3741
public BusinessDateResponse getBusinessDate(String type) {
@@ -49,14 +53,12 @@ public void updateBusinessDate(String type, String date) {
4953
}
5054

5155
public void runAt(String date, Runnable action) {
52-
BusinessDateResponse originalDate = getBusinessDate("BUSINESS_DATE");
5356
try {
57+
configHelper.updateConfigurationByName(ENABLE_BUSINESS_DATE, true);
5458
updateBusinessDate("BUSINESS_DATE", date);
5559
action.run();
5660
} finally {
57-
if (originalDate != null && originalDate.getDate() != null) {
58-
updateBusinessDate("BUSINESS_DATE", originalDate.getDate().toString());
59-
}
61+
configHelper.updateConfigurationByName(ENABLE_BUSINESS_DATE, false);
6062
}
6163
}
6264
}

integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignClientHelper.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ public FeignClientHelper(FineractFeignClient fineractClient) {
3737
}
3838

3939
public Long createClient() {
40+
return createClient(Utils.dateFormatter.format(Utils.getLocalDateOfTenant()));
41+
}
42+
43+
public Long createClient(String activationDate) {
4044
String externalId = Utils.randomStringGenerator("EXT_", 7);
41-
String activationDate = Utils.dateFormatter.format(Utils.getLocalDateOfTenant());
4245

4346
PostClientsRequest request = new PostClientsRequest()//
4447
.officeId(1L)//

integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignTransactionHelper.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020

2121
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
2222

23+
import java.util.List;
2324
import java.util.Map;
2425
import org.apache.fineract.client.feign.FineractFeignClient;
26+
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse;
27+
import org.apache.fineract.client.models.InlineJobRequest;
2528
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
2629
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
2730
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
@@ -34,6 +37,16 @@ public FeignTransactionHelper(FineractFeignClient fineractClient) {
3437
this.fineractClient = fineractClient;
3538
}
3639

40+
public void executeInlineCOB(Long loanId) {
41+
InlineJobRequest request = new InlineJobRequest().loanIds(List.of(loanId));
42+
ok(() -> fineractClient.inlineJob().executeInlineJob("LOAN_COB", request));
43+
}
44+
45+
public GetLoansLoanIdTransactionsTemplateResponse getPrepaymentAmount(Long loanId, String transactionDate, String dateFormat) {
46+
return ok(() -> fineractClient.loanTransactions().retrieveTransactionTemplate(loanId, "prepayLoan", dateFormat, transactionDate,
47+
"en", null));
48+
}
49+
3750
public Long addRepayment(Long loanId, PostLoansLoanIdTransactionsRequest request) {
3851
PostLoansLoanIdTransactionsResponse response = ok(
3952
() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, request, Map.of("command", "repayment")));

integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/LoanProductTemplates.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,18 @@ default PostLoanProductsRequest fourInstallmentsCumulativeTemplate() {
233233
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY);
234234
}
235235

236+
default PostLoanProductsRequest fourInstallmentsCumulativeWithInterestRecalculation() {
237+
return fourInstallmentsCumulativeTemplate()//
238+
.loanScheduleType(LoanScheduleType.CUMULATIVE.toString())//
239+
.isInterestRecalculationEnabled(true)//
240+
.interestRecalculationCompoundingMethod(0)//
241+
.rescheduleStrategyMethod(RescheduleStrategyMethod.REDUCE_EMI_AMOUNT)//
242+
.recalculationRestFrequencyType(1)//
243+
.recalculationRestFrequencyInterval(0)//
244+
.preClosureInterestCalculationStrategy(1)//
245+
.enableAccrualActivityPosting(true);
246+
}
247+
236248
default PostLoanProductsRequest customizeProduct(PostLoanProductsRequest template,
237249
Function<PostLoanProductsRequest, PostLoanProductsRequest> customizer) {
238250
return customizer.apply(template);

integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/LoanRequestBuilders.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public static PostLoansRequest applyCumulativeLoan(Long clientId, Long productId
6767
.amortizationType(LoanTestData.AmortizationType.EQUAL_INSTALLMENTS)//
6868
.interestType(LoanTestData.InterestType.DECLINING_BALANCE)//
6969
.interestCalculationPeriodType(LoanTestData.InterestCalculationPeriodType.DAILY)//
70-
.transactionProcessingStrategyCode("DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY")//
70+
.transactionProcessingStrategyCode("due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy")//
7171
.loanType("individual")//
7272
.locale(LoanTestData.LOCALE)//
7373
.dateFormat(LoanTestData.DATETIME_PATTERN);

integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/LoanTestData.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public static class OutstandingAmounts {
111111

112112
public static final class AmortizationType {
113113

114+
public static final Integer EQUAL_PRINCIPAL = 0;
114115
public static final Integer EQUAL_INSTALLMENTS = 1;
115116

116117
private AmortizationType() {}

0 commit comments

Comments
 (0)