FINERACT-2232: add income capitalization principal handling and repayment schedule calculations on loans
This commit is contained in:
+2
-2
@@ -43,8 +43,8 @@ public class AcmeNoteWritePlatformService implements NoteWritePlatformService, I
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createLoanNote(Long loanId, String note) {
|
public void createLoanTransactionNote(Long loanTransactionId, String note) {
|
||||||
throw new UnsupportedOperationException("createLoanNote() is not yet implemented.");
|
throw new UnsupportedOperationException("createLoanTransactionNote() is not yet implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+6
@@ -310,6 +310,12 @@ public final class DateUtils {
|
|||||||
return isBeforeBusinessDate(localDate);
|
return isBeforeBusinessDate(localDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isDateInRangeFromInclusiveToExclusive(final LocalDate fromInclusive, final LocalDate upToNotInclusive,
|
||||||
|
final LocalDate target) {
|
||||||
|
return (DateUtils.isEqual(target, fromInclusive) || DateUtils.isAfter(target, fromInclusive))
|
||||||
|
&& DateUtils.isBefore(target, upToNotInclusive);
|
||||||
|
}
|
||||||
|
|
||||||
public static int compare(LocalDate first, LocalDate second) {
|
public static int compare(LocalDate first, LocalDate second) {
|
||||||
return compare(first, second, true);
|
return compare(first, second, true);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ public interface NoteWritePlatformService {
|
|||||||
|
|
||||||
CommandProcessingResult createNote(JsonCommand command);
|
CommandProcessingResult createNote(JsonCommand command);
|
||||||
|
|
||||||
void createLoanNote(Long loanId, String note);
|
void createLoanTransactionNote(Long loanTransactionId, String note);
|
||||||
|
|
||||||
CommandProcessingResult updateNote(JsonCommand command);
|
CommandProcessingResult updateNote(JsonCommand command);
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -148,7 +148,7 @@ public class EventCheckHelper {
|
|||||||
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId()).isEqualTo(body.getStatus().getId())//
|
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId()).isEqualTo(body.getStatus().getId())//
|
||||||
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getCode()).isEqualTo(body.getStatus().getCode())//
|
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getCode()).isEqualTo(body.getStatus().getCode())//
|
||||||
.extractingData(LoanAccountDataV1::getClientId).isEqualTo(Long.valueOf(body.getClientId()))//
|
.extractingData(LoanAccountDataV1::getClientId).isEqualTo(Long.valueOf(body.getClientId()))//
|
||||||
.extractingBigDecimal(LoanAccountDataV1::getApprovedPrincipal).isEqualTo(BigDecimal.valueOf(body.getApprovedPrincipal()))//
|
.extractingBigDecimal(LoanAccountDataV1::getApprovedPrincipal).isEqualTo(body.getApprovedPrincipal())//
|
||||||
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getTimeline().getApprovedOnDate())//
|
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getTimeline().getApprovedOnDate())//
|
||||||
.isEqualTo(FORMATTER_EVENTS.format(body.getTimeline().getApprovedOnDate()))//
|
.isEqualTo(FORMATTER_EVENTS.format(body.getTimeline().getApprovedOnDate()))//
|
||||||
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
|
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
|
||||||
@@ -198,7 +198,7 @@ public class EventCheckHelper {
|
|||||||
Long clientIdActual = loanAccountDataV1.getClientId();
|
Long clientIdActual = loanAccountDataV1.getClientId();
|
||||||
Long clientIdExpected = body.getClientId();
|
Long clientIdExpected = body.getClientId();
|
||||||
BigDecimal principalDisbursedActual = loanAccountDataV1.getSummary().getPrincipalDisbursed();
|
BigDecimal principalDisbursedActual = loanAccountDataV1.getSummary().getPrincipalDisbursed();
|
||||||
Double principalDisbursedExpectedDouble = body.getSummary().getPrincipalDisbursed();
|
Double principalDisbursedExpectedDouble = body.getSummary().getPrincipalDisbursed().doubleValue();
|
||||||
BigDecimal principalDisbursedExpected = BigDecimal.valueOf(principalDisbursedExpectedDouble);
|
BigDecimal principalDisbursedExpected = BigDecimal.valueOf(principalDisbursedExpectedDouble);
|
||||||
String actualDisbursementDateActual = loanAccountDataV1.getTimeline().getActualDisbursementDate();
|
String actualDisbursementDateActual = loanAccountDataV1.getTimeline().getActualDisbursementDate();
|
||||||
String actualDisbursementDateExpected = FORMATTER_EVENTS.format(body.getTimeline().getActualDisbursementDate());
|
String actualDisbursementDateExpected = FORMATTER_EVENTS.format(body.getTimeline().getActualDisbursementDate());
|
||||||
@@ -210,7 +210,7 @@ public class EventCheckHelper {
|
|||||||
.getTotalUnpaidPayableNotDueInterest();
|
.getTotalUnpaidPayableNotDueInterest();
|
||||||
BigDecimal totalUnpaidPayableNotDueInterestExpected = body.getSummary().getTotalUnpaidPayableNotDueInterest();
|
BigDecimal totalUnpaidPayableNotDueInterestExpected = body.getSummary().getTotalUnpaidPayableNotDueInterest();
|
||||||
BigDecimal totalInterestPaymentWaiverActual = loanAccountDataV1.getSummary().getTotalInterestPaymentWaiver();
|
BigDecimal totalInterestPaymentWaiverActual = loanAccountDataV1.getSummary().getTotalInterestPaymentWaiver();
|
||||||
Double totalInterestPaymentWaiverExpectedDouble = body.getSummary().getTotalInterestPaymentWaiver();
|
Double totalInterestPaymentWaiverExpectedDouble = body.getSummary().getTotalInterestPaymentWaiver().doubleValue();
|
||||||
BigDecimal totalInterestPaymentWaiverExpected = new BigDecimal(totalInterestPaymentWaiverExpectedDouble,
|
BigDecimal totalInterestPaymentWaiverExpected = new BigDecimal(totalInterestPaymentWaiverExpectedDouble,
|
||||||
MathContext.DECIMAL64);
|
MathContext.DECIMAL64);
|
||||||
BigDecimal delinquentInterestActual = loanAccountDataV1.getDelinquent().getDelinquentInterest();
|
BigDecimal delinquentInterestActual = loanAccountDataV1.getDelinquent().getDelinquentInterest();
|
||||||
@@ -296,7 +296,7 @@ public class EventCheckHelper {
|
|||||||
eventAssertion.assertEvent(LoanDisbursalTransactionEvent.class, disbursementTransaction.getId())//
|
eventAssertion.assertEvent(LoanDisbursalTransactionEvent.class, disbursementTransaction.getId())//
|
||||||
.extractingData(LoanTransactionDataV1::getLoanId).isEqualTo(body.getId())//
|
.extractingData(LoanTransactionDataV1::getLoanId).isEqualTo(body.getId())//
|
||||||
.extractingData(LoanTransactionDataV1::getDate).isEqualTo(FORMATTER_EVENTS.format(disbursementTransaction.getDate()))//
|
.extractingData(LoanTransactionDataV1::getDate).isEqualTo(FORMATTER_EVENTS.format(disbursementTransaction.getDate()))//
|
||||||
.extractingBigDecimal(LoanTransactionDataV1::getAmount).isEqualTo(BigDecimal.valueOf(disbursementTransaction.getAmount()));//
|
.extractingBigDecimal(LoanTransactionDataV1::getAmount).isEqualTo(disbursementTransaction.getAmount());//
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEventCheck(
|
public EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEventCheck(
|
||||||
@@ -325,7 +325,7 @@ public class EventCheckHelper {
|
|||||||
EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> eventBuilder = eventAssertion.assertEvent(eventClass, transactionId);
|
EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> eventBuilder = eventAssertion.assertEvent(eventClass, transactionId);
|
||||||
eventBuilder.extractingData(LoanTransactionDataV1::getLoanId).isEqualTo(loanDetailsResponse.body().getId())//
|
eventBuilder.extractingData(LoanTransactionDataV1::getLoanId).isEqualTo(loanDetailsResponse.body().getId())//
|
||||||
.extractingData(LoanTransactionDataV1::getDate).isEqualTo(FORMATTER_EVENTS.format(transactionFound.getDate()))//
|
.extractingData(LoanTransactionDataV1::getDate).isEqualTo(FORMATTER_EVENTS.format(transactionFound.getDate()))//
|
||||||
.extractingBigDecimal(LoanTransactionDataV1::getAmount).isEqualTo(BigDecimal.valueOf(transactionFound.getAmount()))//
|
.extractingBigDecimal(LoanTransactionDataV1::getAmount).isEqualTo(transactionFound.getAmount())//
|
||||||
.extractingData(LoanTransactionDataV1::getExternalOwnerId).isEqualTo(externalOwnerId);//
|
.extractingData(LoanTransactionDataV1::getExternalOwnerId).isEqualTo(externalOwnerId);//
|
||||||
return eventBuilder;
|
return eventBuilder;
|
||||||
}
|
}
|
||||||
@@ -420,8 +420,8 @@ public class EventCheckHelper {
|
|||||||
.getGlobalConfiguration("outstanding-interest-calculation-strategy-for-external-asset-transfer");
|
.getGlobalConfiguration("outstanding-interest-calculation-strategy-for-external-asset-transfer");
|
||||||
if ("PAYABLE_OUTSTANDING_INTEREST".equals(outstandingInterestStrategy.getStringValue())) {
|
if ("PAYABLE_OUTSTANDING_INTEREST".equals(outstandingInterestStrategy.getStringValue())) {
|
||||||
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", null, null).execute();
|
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", null, null).execute();
|
||||||
totalOutstandingBalanceAmountExpected = BigDecimal.valueOf(loanDetails.body().getSummary().getTotalOutstanding());
|
totalOutstandingBalanceAmountExpected = loanDetails.body().getSummary().getTotalOutstanding();
|
||||||
outstandingInterestPortionExpected = BigDecimal.valueOf(loanDetails.body().getSummary().getInterestOutstanding());
|
outstandingInterestPortionExpected = loanDetails.body().getSummary().getInterestOutstanding();
|
||||||
}
|
}
|
||||||
|
|
||||||
String ownerExternalIdExpected = filtered.getStatus().getValue().equals("BUYBACK") ? null : filtered.getOwner().getExternalId();
|
String ownerExternalIdExpected = filtered.getStatus().getValue().equals("BUYBACK") ? null : filtered.getOwner().getExternalId();
|
||||||
|
|||||||
+1
-1
@@ -161,7 +161,7 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
|
|||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
|
||||||
String dateActual = formatter.format(date);
|
String dateActual = formatter.format(date);
|
||||||
|
|
||||||
Double amountActual = transactions.get(i).getAmount();
|
Double amountActual = transactions.get(i).getAmount().doubleValue();
|
||||||
|
|
||||||
if (dateActual.equals(transactionDate) && amountActual.equals(transactionAmount)) {
|
if (dateActual.equals(transactionDate) && amountActual.equals(transactionAmount)) {
|
||||||
transactionMetConditions = transactions.get(i);
|
transactionMetConditions = transactions.get(i);
|
||||||
|
|||||||
+2
-2
@@ -654,7 +654,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
|
|||||||
Long loanLevelDelinquencyRangeIdExpected = delinquencyRange.getId();
|
Long loanLevelDelinquencyRangeIdExpected = delinquencyRange.getId();
|
||||||
String loanLevelDelinquencyRangeExpected = delinquencyRange.getClassification();
|
String loanLevelDelinquencyRangeExpected = delinquencyRange.getClassification();
|
||||||
String loanLevelDelinquentDateExpected = FORMATTER.format(delinquent.getDelinquentDate());
|
String loanLevelDelinquentDateExpected = FORMATTER.format(delinquent.getDelinquentDate());
|
||||||
BigDecimal loanLevelTotalAmountExpected = BigDecimal.valueOf(delinquent.getDelinquentAmount());
|
BigDecimal loanLevelTotalAmountExpected = delinquent.getDelinquentAmount();
|
||||||
|
|
||||||
assertThat(loanLevelDelinquencyRangeId)//
|
assertThat(loanLevelDelinquencyRangeId)//
|
||||||
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent4(loanLevelDelinquencyRangeId,
|
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent4(loanLevelDelinquencyRangeId,
|
||||||
@@ -715,7 +715,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
|
|||||||
ErrorHelper.checkSuccessfulApiCall(loanDetails);
|
ErrorHelper.checkSuccessfulApiCall(loanDetails);
|
||||||
|
|
||||||
Double expectedLastRepaymentAmount1 = Double.valueOf(expectedLastRepaymentAmount);
|
Double expectedLastRepaymentAmount1 = Double.valueOf(expectedLastRepaymentAmount);
|
||||||
Double actualLastRepaymentAmount = loanDetails.body().getDelinquent().getLastRepaymentAmount();
|
Double actualLastRepaymentAmount = loanDetails.body().getDelinquent().getLastRepaymentAmount().doubleValue();
|
||||||
String actualLastRepaymentDate = FORMATTER.format(loanDetails.body().getDelinquent().getLastRepaymentDate());
|
String actualLastRepaymentDate = FORMATTER.format(loanDetails.body().getDelinquent().getLastRepaymentDate());
|
||||||
|
|
||||||
assertThat(actualLastRepaymentAmount)//
|
assertThat(actualLastRepaymentAmount)//
|
||||||
|
|||||||
+2
-2
@@ -550,7 +550,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
|
|||||||
BigDecimal lastExpectedAmount = new BigDecimal(totalAmount).setScale(0, RoundingMode.HALF_DOWN);
|
BigDecimal lastExpectedAmount = new BigDecimal(totalAmount).setScale(0, RoundingMode.HALF_DOWN);
|
||||||
|
|
||||||
for (int i = 1; i < periods.size(); i++) {
|
for (int i = 1; i < periods.size(); i++) {
|
||||||
BigDecimal actualAmount = new BigDecimal(periods.get(i).getPrincipalOriginalDue()).setScale(0, RoundingMode.HALF_DOWN);
|
BigDecimal actualAmount = periods.get(i).getPrincipalOriginalDue().setScale(0, RoundingMode.HALF_DOWN);
|
||||||
|
|
||||||
if (i == periods.size() - 1) {
|
if (i == periods.size() - 1) {
|
||||||
assertThat(actualAmount.compareTo(lastExpectedAmount))
|
assertThat(actualAmount.compareTo(lastExpectedAmount))
|
||||||
@@ -613,7 +613,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
|
|||||||
eventAssertionBuilder
|
eventAssertionBuilder
|
||||||
.extractingBigDecimal(
|
.extractingBigDecimal(
|
||||||
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getAmount())
|
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getAmount())
|
||||||
.isEqualTo(BigDecimal.valueOf(targetTransaction.getAmount()));
|
.isEqualTo(targetTransaction.getAmount());
|
||||||
eventAssertionBuilder
|
eventAssertionBuilder
|
||||||
.extractingData(
|
.extractingData(
|
||||||
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
|
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
|
||||||
|
|||||||
+79
-74
@@ -1121,9 +1121,9 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||||
|
|
||||||
GetLoansLoanIdDelinquencySummary delinquent = loanDetailsResponse.body().getDelinquent();
|
GetLoansLoanIdDelinquencySummary delinquent = loanDetailsResponse.body().getDelinquent();
|
||||||
String lastPaymentAmountActual = String.valueOf(delinquent.getLastPaymentAmount());
|
String lastPaymentAmountActual = new Utils.DoubleFormatter(delinquent.getLastPaymentAmount().doubleValue()).format();
|
||||||
String lastPaymentDateActual = FORMATTER.format(delinquent.getLastPaymentDate());
|
String lastPaymentDateActual = FORMATTER.format(delinquent.getLastPaymentDate());
|
||||||
String lastRepaymentAmountActual = String.valueOf(delinquent.getLastRepaymentAmount());
|
String lastRepaymentAmountActual = new Utils.DoubleFormatter(delinquent.getLastRepaymentAmount().doubleValue()).format();
|
||||||
String lastRepaymentDateActual = FORMATTER.format(delinquent.getLastRepaymentDate());
|
String lastRepaymentDateActual = FORMATTER.format(delinquent.getLastRepaymentDate());
|
||||||
|
|
||||||
assertThat(lastPaymentAmountActual)
|
assertThat(lastPaymentAmountActual)
|
||||||
@@ -1832,7 +1832,7 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||||
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
|
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
|
||||||
|
|
||||||
Double totalOutstandingActual = loanDetailsResponse.body().getSummary().getTotalOutstanding();
|
Double totalOutstandingActual = loanDetailsResponse.body().getSummary().getTotalOutstanding().doubleValue();
|
||||||
assertThat(totalOutstandingActual)
|
assertThat(totalOutstandingActual)
|
||||||
.as(ErrorMessageHelper.wrongAmountInTotalOutstanding(totalOutstandingActual, totalOutstandingExpected))
|
.as(ErrorMessageHelper.wrongAmountInTotalOutstanding(totalOutstandingActual, totalOutstandingExpected))
|
||||||
.isEqualTo(totalOutstandingExpected);
|
.isEqualTo(totalOutstandingExpected);
|
||||||
@@ -1847,8 +1847,8 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||||
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
|
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
|
||||||
|
|
||||||
Double totalOverpaidActual = loanDetailsResponse.body().getTotalOverpaid();
|
Double totalOverpaidActual = loanDetailsResponse.body().getTotalOverpaid().doubleValue();
|
||||||
Double totalOutstandingActual = loanDetailsResponse.body().getSummary().getTotalOutstanding();
|
Double totalOutstandingActual = loanDetailsResponse.body().getSummary().getTotalOutstanding().doubleValue();
|
||||||
double totalOutstandingExpected = 0.0;
|
double totalOutstandingExpected = 0.0;
|
||||||
assertThat(totalOutstandingActual)
|
assertThat(totalOutstandingActual)
|
||||||
.as(ErrorMessageHelper.wrongAmountInTotalOutstanding(totalOutstandingActual, totalOutstandingExpected))
|
.as(ErrorMessageHelper.wrongAmountInTotalOutstanding(totalOutstandingActual, totalOutstandingExpected))
|
||||||
@@ -1867,7 +1867,7 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||||
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
|
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
|
||||||
|
|
||||||
Double totalOverdueActual = loanDetailsResponse.body().getSummary().getTotalOverdue();
|
Double totalOverdueActual = loanDetailsResponse.body().getSummary().getTotalOverdue().doubleValue();
|
||||||
assertThat(totalOverdueActual).as(ErrorMessageHelper.wrongAmountInTotalOverdue(totalOverdueActual, totalOverdueExpected))
|
assertThat(totalOverdueActual).as(ErrorMessageHelper.wrongAmountInTotalOverdue(totalOverdueActual, totalOverdueExpected))
|
||||||
.isEqualTo(totalOverdueExpected);
|
.isEqualTo(totalOverdueExpected);
|
||||||
}
|
}
|
||||||
@@ -1881,7 +1881,7 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||||
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
|
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
|
||||||
|
|
||||||
Double lastPaymentAmountActual = loanDetailsResponse.body().getDelinquent().getLastPaymentAmount();
|
Double lastPaymentAmountActual = loanDetailsResponse.body().getDelinquent().getLastPaymentAmount().doubleValue();
|
||||||
assertThat(lastPaymentAmountActual)
|
assertThat(lastPaymentAmountActual)
|
||||||
.as(ErrorMessageHelper.wrongLastPaymentAmount(lastPaymentAmountActual, lastPaymentAmountExpected))
|
.as(ErrorMessageHelper.wrongLastPaymentAmount(lastPaymentAmountActual, lastPaymentAmountExpected))
|
||||||
.isEqualTo(lastPaymentAmountExpected);
|
.isEqualTo(lastPaymentAmountExpected);
|
||||||
@@ -2173,12 +2173,13 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
actualValues.add(t.getDueDate() == null ? null : FORMATTER.format(t.getDueDate()));
|
actualValues.add(t.getDueDate() == null ? null : FORMATTER.format(t.getDueDate()));
|
||||||
actualValues.add(t.getChargeCalculationType().getValue() == null ? null : t.getChargeCalculationType().getValue());
|
actualValues.add(t.getChargeCalculationType().getValue() == null ? null : t.getChargeCalculationType().getValue());
|
||||||
|
|
||||||
actualValues.add(t.getAmount() == null ? null : new Utils.DoubleFormatter(t.getAmount()).format());
|
actualValues.add(t.getAmount() == null ? null : new Utils.DoubleFormatter(t.getAmount().doubleValue()).format());
|
||||||
|
|
||||||
actualValues.add(t.getAmountPaid() == null ? null : new Utils.DoubleFormatter(t.getAmountPaid()).format());
|
actualValues.add(t.getAmountPaid() == null ? null : new Utils.DoubleFormatter(t.getAmountPaid().doubleValue()).format());
|
||||||
actualValues.add(t.getAmountWaived() == null ? null : new Utils.DoubleFormatter(t.getAmountWaived()).format());
|
actualValues.add(t.getAmountWaived() == null ? null : new Utils.DoubleFormatter(t.getAmountWaived().doubleValue()).format());
|
||||||
|
|
||||||
actualValues.add(t.getAmountOutstanding() == null ? null : new Utils.DoubleFormatter(t.getAmountOutstanding()).format());
|
actualValues.add(
|
||||||
|
t.getAmountOutstanding() == null ? null : new Utils.DoubleFormatter(t.getAmountOutstanding().doubleValue()).format());
|
||||||
return actualValues;
|
return actualValues;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
@@ -2681,9 +2682,9 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
|
||||||
|
|
||||||
GetLoansLoanIdDelinquencySummary delinquent = loanDetailsResponse.body().getDelinquent();
|
GetLoansLoanIdDelinquencySummary delinquent = loanDetailsResponse.body().getDelinquent();
|
||||||
String lastPaymentAmountActual = String.valueOf(delinquent.getLastPaymentAmount());
|
String lastPaymentAmountActual = new Utils.DoubleFormatter(delinquent.getLastPaymentAmount().doubleValue()).format();
|
||||||
String lastPaymentDateActual = FORMATTER.format(delinquent.getLastPaymentDate());
|
String lastPaymentDateActual = FORMATTER.format(delinquent.getLastPaymentDate());
|
||||||
String lastRepaymentAmountActual = String.valueOf(delinquent.getLastRepaymentAmount());
|
String lastRepaymentAmountActual = new Utils.DoubleFormatter(delinquent.getLastRepaymentAmount().doubleValue()).format();
|
||||||
String lastRepaymentDateActual = FORMATTER.format(delinquent.getLastRepaymentDate());
|
String lastRepaymentDateActual = FORMATTER.format(delinquent.getLastRepaymentDate());
|
||||||
|
|
||||||
assertThat(lastPaymentAmountActual)
|
assertThat(lastPaymentAmountActual)
|
||||||
@@ -2923,7 +2924,7 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
ErrorHelper.checkSuccessfulApiCall(loanDetails);
|
ErrorHelper.checkSuccessfulApiCall(loanDetails);
|
||||||
|
|
||||||
Double expectedAmountParsed = Double.parseDouble(expectedAmount);
|
Double expectedAmountParsed = Double.parseDouble(expectedAmount);
|
||||||
Double totalRepaymentTransaction = loanDetails.body().getSummary().getTotalRepaymentTransaction();
|
Double totalRepaymentTransaction = loanDetails.body().getSummary().getTotalRepaymentTransaction().doubleValue();
|
||||||
|
|
||||||
assertThat(totalRepaymentTransaction)
|
assertThat(totalRepaymentTransaction)
|
||||||
.as(ErrorMessageHelper.wrongAmountInTotalRepaymentTransaction(totalRepaymentTransaction, expectedAmountParsed))
|
.as(ErrorMessageHelper.wrongAmountInTotalRepaymentTransaction(totalRepaymentTransaction, expectedAmountParsed))
|
||||||
@@ -3310,8 +3311,8 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
final Double disbursementPrincipalAmount = Double.valueOf(loanData.get(19));
|
final Double disbursementPrincipalAmount = Double.valueOf(loanData.get(19));
|
||||||
|
|
||||||
List<PostLoansDisbursementData> disbursementDetail = new ArrayList<>();
|
List<PostLoansDisbursementData> disbursementDetail = new ArrayList<>();
|
||||||
disbursementDetail.add(
|
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDate)
|
||||||
new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDate).principal(disbursementPrincipalAmount));
|
.principal(BigDecimal.valueOf(disbursementPrincipalAmount)));
|
||||||
|
|
||||||
createFullyCustomizedLoanWithChargesExpectsTrancheDisbursementDetails(loanData, disbursementDetail);
|
createFullyCustomizedLoanWithChargesExpectsTrancheDisbursementDetails(loanData, disbursementDetail);
|
||||||
}
|
}
|
||||||
@@ -3325,9 +3326,9 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
|
|
||||||
List<PostLoansDisbursementData> disbursementDetail = new ArrayList<>();
|
List<PostLoansDisbursementData> disbursementDetail = new ArrayList<>();
|
||||||
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDateFirstDisbursal)
|
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDateFirstDisbursal)
|
||||||
.principal(disbursementPrincipalAmountFirstDisbursal));
|
.principal(BigDecimal.valueOf(disbursementPrincipalAmountFirstDisbursal)));
|
||||||
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDateSecondDisbursal)
|
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDateSecondDisbursal)
|
||||||
.principal(disbursementPrincipalAmountSecondDisbursal));
|
.principal(BigDecimal.valueOf(disbursementPrincipalAmountSecondDisbursal)));
|
||||||
|
|
||||||
createFullyCustomizedLoanWithChargesExpectsTrancheDisbursementDetails(loanData, disbursementDetail);
|
createFullyCustomizedLoanWithChargesExpectsTrancheDisbursementDetails(loanData, disbursementDetail);
|
||||||
}
|
}
|
||||||
@@ -3418,8 +3419,8 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
final Double disbursementPrincipalAmount = Double.valueOf(loanData.get(17));
|
final Double disbursementPrincipalAmount = Double.valueOf(loanData.get(17));
|
||||||
|
|
||||||
List<PostLoansDisbursementData> disbursementDetail = new ArrayList<>();
|
List<PostLoansDisbursementData> disbursementDetail = new ArrayList<>();
|
||||||
disbursementDetail.add(
|
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDate)
|
||||||
new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDate).principal(disbursementPrincipalAmount));
|
.principal(BigDecimal.valueOf(disbursementPrincipalAmount)));
|
||||||
|
|
||||||
createFullyCustomizedLoanExpectsTrancheDisbursementDetails(loanData, disbursementDetail);
|
createFullyCustomizedLoanExpectsTrancheDisbursementDetails(loanData, disbursementDetail);
|
||||||
}
|
}
|
||||||
@@ -3433,9 +3434,9 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
|
|
||||||
List<PostLoansDisbursementData> disbursementDetail = new ArrayList<>();
|
List<PostLoansDisbursementData> disbursementDetail = new ArrayList<>();
|
||||||
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDateFirstDisbursal)
|
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDateFirstDisbursal)
|
||||||
.principal(disbursementPrincipalAmountFirstDisbursal));
|
.principal(BigDecimal.valueOf(disbursementPrincipalAmountFirstDisbursal)));
|
||||||
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDateSecondDisbursal)
|
disbursementDetail.add(new PostLoansDisbursementData().expectedDisbursementDate(expectedDisbursementDateSecondDisbursal)
|
||||||
.principal(disbursementPrincipalAmountSecondDisbursal));
|
.principal(BigDecimal.valueOf(disbursementPrincipalAmountSecondDisbursal)));
|
||||||
|
|
||||||
createFullyCustomizedLoanExpectsTrancheDisbursementDetails(loanData, disbursementDetail);
|
createFullyCustomizedLoanExpectsTrancheDisbursementDetails(loanData, disbursementDetail);
|
||||||
}
|
}
|
||||||
@@ -3698,16 +3699,20 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
switch (headerName) {
|
switch (headerName) {
|
||||||
case "Transaction date" -> actualValues.add(t.getDate() == null ? null : FORMATTER.format(t.getDate()));
|
case "Transaction date" -> actualValues.add(t.getDate() == null ? null : FORMATTER.format(t.getDate()));
|
||||||
case "Transaction Type" -> actualValues.add(t.getType().getValue() == null ? null : t.getType().getValue());
|
case "Transaction Type" -> actualValues.add(t.getType().getValue() == null ? null : t.getType().getValue());
|
||||||
case "Amount" -> actualValues.add(t.getAmount() == null ? null : String.valueOf(t.getAmount()));
|
case "Amount" ->
|
||||||
case "Principal" -> actualValues.add(t.getPrincipalPortion() == null ? null : String.valueOf(t.getPrincipalPortion()));
|
actualValues.add(t.getAmount() == null ? null : new Utils.DoubleFormatter(t.getAmount().doubleValue()).format());
|
||||||
case "Interest" -> actualValues.add(t.getInterestPortion() == null ? null : String.valueOf(t.getInterestPortion()));
|
case "Principal" -> actualValues.add(
|
||||||
case "Fees" -> actualValues.add(t.getFeeChargesPortion() == null ? null : String.valueOf(t.getFeeChargesPortion()));
|
t.getPrincipalPortion() == null ? null : new Utils.DoubleFormatter(t.getPrincipalPortion().doubleValue()).format());
|
||||||
case "Penalties" ->
|
case "Interest" -> actualValues.add(
|
||||||
actualValues.add(t.getPenaltyChargesPortion() == null ? null : String.valueOf(t.getPenaltyChargesPortion()));
|
t.getInterestPortion() == null ? null : new Utils.DoubleFormatter(t.getInterestPortion().doubleValue()).format());
|
||||||
case "Loan Balance" ->
|
case "Fees" -> actualValues.add(t.getFeeChargesPortion() == null ? null
|
||||||
actualValues.add(t.getOutstandingLoanBalance() == null ? null : String.valueOf(t.getOutstandingLoanBalance()));
|
: new Utils.DoubleFormatter(t.getFeeChargesPortion().doubleValue()).format());
|
||||||
case "Overpayment" ->
|
case "Penalties" -> actualValues.add(t.getPenaltyChargesPortion() == null ? null
|
||||||
actualValues.add(t.getOverpaymentPortion() == null ? null : String.valueOf(t.getOverpaymentPortion()));
|
: new Utils.DoubleFormatter(t.getPenaltyChargesPortion().doubleValue()).format());
|
||||||
|
case "Loan Balance" -> actualValues.add(t.getOutstandingLoanBalance() == null ? null
|
||||||
|
: new Utils.DoubleFormatter(t.getOutstandingLoanBalance().doubleValue()).format());
|
||||||
|
case "Overpayment" -> actualValues.add(t.getOverpaymentPortion() == null ? null
|
||||||
|
: new Utils.DoubleFormatter(t.getOverpaymentPortion().doubleValue()).format());
|
||||||
case "Reverted" -> actualValues.add(t.getManuallyReversed() == null ? null : String.valueOf(t.getManuallyReversed()));
|
case "Reverted" -> actualValues.add(t.getManuallyReversed() == null ? null : String.valueOf(t.getManuallyReversed()));
|
||||||
case "Replayed" -> {
|
case "Replayed" -> {
|
||||||
boolean hasReplayed = t.getTransactionRelations().stream().anyMatch(e -> "REPLAYED".equals(e.getRelationType()));
|
boolean hasReplayed = t.getTransactionRelations().stream().anyMatch(e -> "REPLAYED".equals(e.getRelationType()));
|
||||||
@@ -3748,27 +3753,27 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
case "Paid date" -> actualValues.add(repaymentPeriod.getObligationsMetOnDate() == null ? null
|
case "Paid date" -> actualValues.add(repaymentPeriod.getObligationsMetOnDate() == null ? null
|
||||||
: FORMATTER.format(repaymentPeriod.getObligationsMetOnDate()));
|
: FORMATTER.format(repaymentPeriod.getObligationsMetOnDate()));
|
||||||
case "Balance of loan" -> actualValues.add(repaymentPeriod.getPrincipalLoanBalanceOutstanding() == null ? null
|
case "Balance of loan" -> actualValues.add(repaymentPeriod.getPrincipalLoanBalanceOutstanding() == null ? null
|
||||||
: String.valueOf(repaymentPeriod.getPrincipalLoanBalanceOutstanding()));
|
: new Utils.DoubleFormatter(repaymentPeriod.getPrincipalLoanBalanceOutstanding().doubleValue()).format());
|
||||||
case "Principal due" ->
|
case "Principal due" -> actualValues.add(repaymentPeriod.getPrincipalDue() == null ? null
|
||||||
actualValues.add(repaymentPeriod.getPrincipalDue() == null ? null : String.valueOf(repaymentPeriod.getPrincipalDue()));
|
: new Utils.DoubleFormatter(repaymentPeriod.getPrincipalDue().doubleValue()).format());
|
||||||
case "Interest" ->
|
case "Interest" -> actualValues.add(repaymentPeriod.getInterestDue() == null ? null
|
||||||
actualValues.add(repaymentPeriod.getInterestDue() == null ? null : String.valueOf(repaymentPeriod.getInterestDue()));
|
: new Utils.DoubleFormatter(repaymentPeriod.getInterestDue().doubleValue()).format());
|
||||||
case "Fees" -> actualValues.add(repaymentPeriod.getFeeChargesDue() == null ? null
|
case "Fees" -> actualValues.add(repaymentPeriod.getFeeChargesDue() == null ? null
|
||||||
: new Utils.DoubleFormatter(repaymentPeriod.getFeeChargesDue()).format());
|
: new Utils.DoubleFormatter(repaymentPeriod.getFeeChargesDue().doubleValue()).format());
|
||||||
case "Penalties" -> actualValues.add(repaymentPeriod.getPenaltyChargesDue() == null ? null
|
case "Penalties" -> actualValues.add(repaymentPeriod.getPenaltyChargesDue() == null ? null
|
||||||
: new Utils.DoubleFormatter(repaymentPeriod.getPenaltyChargesDue()).format());
|
: new Utils.DoubleFormatter(repaymentPeriod.getPenaltyChargesDue().doubleValue()).format());
|
||||||
case "Due" -> actualValues.add(repaymentPeriod.getTotalDueForPeriod() == null ? null
|
case "Due" -> actualValues.add(repaymentPeriod.getTotalDueForPeriod() == null ? null
|
||||||
: new Utils.DoubleFormatter(repaymentPeriod.getTotalDueForPeriod()).format());
|
: new Utils.DoubleFormatter(repaymentPeriod.getTotalDueForPeriod().doubleValue()).format());
|
||||||
case "Paid" -> actualValues.add(
|
case "Paid" -> actualValues.add(repaymentPeriod.getTotalPaidForPeriod() == null ? null
|
||||||
repaymentPeriod.getTotalPaidForPeriod() == null ? null : String.valueOf(repaymentPeriod.getTotalPaidForPeriod()));
|
: new Utils.DoubleFormatter(repaymentPeriod.getTotalPaidForPeriod().doubleValue()).format());
|
||||||
case "In advance" -> actualValues.add(repaymentPeriod.getTotalPaidInAdvanceForPeriod() == null ? null
|
case "In advance" -> actualValues.add(repaymentPeriod.getTotalPaidInAdvanceForPeriod() == null ? null
|
||||||
: String.valueOf(repaymentPeriod.getTotalPaidInAdvanceForPeriod()));
|
: new Utils.DoubleFormatter(repaymentPeriod.getTotalPaidInAdvanceForPeriod().doubleValue()).format());
|
||||||
case "Late" -> actualValues.add(repaymentPeriod.getTotalPaidLateForPeriod() == null ? null
|
case "Late" -> actualValues.add(repaymentPeriod.getTotalPaidLateForPeriod() == null ? null
|
||||||
: String.valueOf(repaymentPeriod.getTotalPaidLateForPeriod()));
|
: new Utils.DoubleFormatter(repaymentPeriod.getTotalPaidLateForPeriod().doubleValue()).format());
|
||||||
case "Waived" -> actualValues.add(repaymentPeriod.getTotalWaivedForPeriod() == null ? null
|
case "Waived" -> actualValues.add(repaymentPeriod.getTotalWaivedForPeriod() == null ? null
|
||||||
: String.valueOf(repaymentPeriod.getTotalWaivedForPeriod()));
|
: new Utils.DoubleFormatter(repaymentPeriod.getTotalWaivedForPeriod().doubleValue()).format());
|
||||||
case "Outstanding" -> actualValues.add(repaymentPeriod.getTotalOutstandingForPeriod() == null ? null
|
case "Outstanding" -> actualValues.add(repaymentPeriod.getTotalOutstandingForPeriod() == null ? null
|
||||||
: new Utils.DoubleFormatter(repaymentPeriod.getTotalOutstandingForPeriod()).format());
|
: new Utils.DoubleFormatter(repaymentPeriod.getTotalOutstandingForPeriod().doubleValue()).format());
|
||||||
default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName));
|
default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3784,7 +3789,7 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
List<GetLoansLoanIdRepaymentPeriod> periods = repaymentSchedule.getPeriods();
|
List<GetLoansLoanIdRepaymentPeriod> periods = repaymentSchedule.getPeriods();
|
||||||
for (GetLoansLoanIdRepaymentPeriod period : periods) {
|
for (GetLoansLoanIdRepaymentPeriod period : periods) {
|
||||||
if (null != period.getTotalPaidForPeriod()) {
|
if (null != period.getTotalPaidForPeriod()) {
|
||||||
paidActual += period.getTotalPaidForPeriod();
|
paidActual += period.getTotalPaidForPeriod().doubleValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BigDecimal paidActualBd = new BigDecimal(paidActual).setScale(2, RoundingMode.HALF_DOWN);
|
BigDecimal paidActualBd = new BigDecimal(paidActual).setScale(2, RoundingMode.HALF_DOWN);
|
||||||
@@ -3793,45 +3798,45 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
String headerName = header.get(i);
|
String headerName = header.get(i);
|
||||||
String expectedValue = expectedAmounts.get(i);
|
String expectedValue = expectedAmounts.get(i);
|
||||||
switch (headerName) {
|
switch (headerName) {
|
||||||
case "Principal due" -> assertThat(repaymentSchedule.getTotalPrincipalExpected())//
|
case "Principal due" -> assertThat(repaymentSchedule.getTotalPrincipalExpected().doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePrincipal(repaymentSchedule.getTotalPrincipalExpected(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePrincipal(
|
||||||
Double.valueOf(expectedValue)))//
|
repaymentSchedule.getTotalPrincipalExpected().doubleValue(), Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
case "Interest" -> assertThat(repaymentSchedule.getTotalInterestCharged())//
|
case "Interest" -> assertThat(repaymentSchedule.getTotalInterestCharged().doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInterest(repaymentSchedule.getTotalInterestCharged(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInterest(
|
||||||
Double.valueOf(expectedValue)))//
|
repaymentSchedule.getTotalInterestCharged().doubleValue(), Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
case "Fees" -> assertThat(repaymentSchedule.getTotalFeeChargesCharged())//
|
case "Fees" -> assertThat(repaymentSchedule.getTotalFeeChargesCharged().doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleFees(repaymentSchedule.getTotalFeeChargesCharged(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleFees(
|
||||||
Double.valueOf(expectedValue)))//
|
repaymentSchedule.getTotalFeeChargesCharged().doubleValue(), Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
case "Penalties" -> assertThat(repaymentSchedule.getTotalPenaltyChargesCharged())//
|
case "Penalties" -> assertThat(repaymentSchedule.getTotalPenaltyChargesCharged().doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePenalties(repaymentSchedule.getTotalPenaltyChargesCharged(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePenalties(
|
||||||
Double.valueOf(expectedValue)))//
|
repaymentSchedule.getTotalPenaltyChargesCharged().doubleValue(), Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
case "Due" -> assertThat(repaymentSchedule.getTotalRepaymentExpected())//
|
case "Due" -> assertThat(repaymentSchedule.getTotalRepaymentExpected().doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleDue(repaymentSchedule.getTotalRepaymentExpected(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleDue(
|
||||||
Double.valueOf(expectedValue)))//
|
repaymentSchedule.getTotalRepaymentExpected().doubleValue(), Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
case "Paid" -> assertThat(paidActualBd.doubleValue())//
|
case "Paid" -> assertThat(paidActualBd.doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePaid(paidActualBd.doubleValue(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePaid(paidActualBd.doubleValue(),
|
||||||
Double.valueOf(expectedValue)))//
|
Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
case "In advance" -> assertThat(repaymentSchedule.getTotalPaidInAdvance())//
|
case "In advance" -> assertThat(repaymentSchedule.getTotalPaidInAdvance().doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInAdvance(repaymentSchedule.getTotalPaidInAdvance(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInAdvance(
|
||||||
|
repaymentSchedule.getTotalPaidInAdvance().doubleValue(), Double.valueOf(expectedValue)))//
|
||||||
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
|
case "Late" -> assertThat(repaymentSchedule.getTotalPaidLate().doubleValue())//
|
||||||
|
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleLate(repaymentSchedule.getTotalPaidLate().doubleValue(),
|
||||||
Double.valueOf(expectedValue)))//
|
Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
case "Late" -> assertThat(repaymentSchedule.getTotalPaidLate())//
|
case "Waived" -> assertThat(repaymentSchedule.getTotalWaived().doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleLate(repaymentSchedule.getTotalPaidLate(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleWaived(repaymentSchedule.getTotalWaived().doubleValue(),
|
||||||
Double.valueOf(expectedValue)))//
|
Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
case "Waived" -> assertThat(repaymentSchedule.getTotalWaived())//
|
case "Outstanding" -> assertThat(repaymentSchedule.getTotalOutstanding().doubleValue())//
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleWaived(repaymentSchedule.getTotalWaived(),
|
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleOutstanding(
|
||||||
Double.valueOf(expectedValue)))//
|
repaymentSchedule.getTotalOutstanding().doubleValue(), Double.valueOf(expectedValue)))//
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
|
||||||
case "Outstanding" -> assertThat(repaymentSchedule.getTotalOutstanding())//
|
|
||||||
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleOutstanding(repaymentSchedule.getTotalOutstanding(),
|
|
||||||
Double.valueOf(expectedValue)))//
|
|
||||||
.isEqualTo(Double.valueOf(expectedValue));//
|
.isEqualTo(Double.valueOf(expectedValue));//
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3853,7 +3858,7 @@ public class LoanStepDef extends AbstractStepDef {
|
|||||||
case "Applicable From" -> actualValues.add(emiVariation.getTermVariationApplicableFrom() == null ? null
|
case "Applicable From" -> actualValues.add(emiVariation.getTermVariationApplicableFrom() == null ? null
|
||||||
: FORMATTER.format(emiVariation.getTermVariationApplicableFrom()));
|
: FORMATTER.format(emiVariation.getTermVariationApplicableFrom()));
|
||||||
case "Decimal Value" ->
|
case "Decimal Value" ->
|
||||||
actualValues.add(emiVariation.getDecimalValue() == null ? null : String.valueOf(emiVariation.getDecimalValue()));
|
actualValues.add(emiVariation.getDecimalValue() == null ? null : new Utils.DoubleFormatter(emiVariation.getDecimalValue().doubleValue()).format());
|
||||||
case "Date Value" ->
|
case "Date Value" ->
|
||||||
actualValues.add(emiVariation.getDateValue() == null ? null : FORMATTER.format(emiVariation.getDateValue()));
|
actualValues.add(emiVariation.getDateValue() == null ? null : FORMATTER.format(emiVariation.getDateValue()));
|
||||||
case "Is Specific To Installment" -> actualValues.add(String.valueOf(emiVariation.getIsSpecificToInstallment()));
|
case "Is Specific To Installment" -> actualValues.add(String.valueOf(emiVariation.getIsSpecificToInstallment()));
|
||||||
|
|||||||
+39
-39
@@ -188,12 +188,12 @@ Feature: Capitalized Income
|
|||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
| ASSET | 112601 | Loans Receivable | 100.0 | |
|
| ASSET | 112601 | Loans Receivable | 100.0 | |
|
||||||
| LIABILITY | 145024 | Deferred Capitalized Income | | 100.0 |
|
| LIABILITY | 145024 | Deferred Capitalized Income | | 100.0 |
|
||||||
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "2 January 2024" with 900.17 EUR transaction amount and system-generated Idempotency key
|
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "2 January 2024" with 1000.17 EUR transaction amount and system-generated Idempotency key
|
||||||
Then Loan Transactions tab has the following data:
|
Then Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||||
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||||
| 02 January 2024 | Repayment | 900.17 | 900.0 | 0.17 | 0.0 | 0.0 | 100.0 | false |
|
| 02 January 2024 | Repayment | 1000.17| 1000.0 | 0.17 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 02 January 2024 | Accrual | 0.17 | 0.0 | 0.17 | 0.0 | 0.0 | 0.0 | false |
|
| 02 January 2024 | Accrual | 0.17 | 0.0 | 0.17 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
| 02 January 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
||||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "02 January 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "02 January 2024" which has the following Journal entries:
|
||||||
@@ -222,12 +222,12 @@ Feature: Capitalized Income
|
|||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
| ASSET | 112601 | Loans Receivable | 100.0 | |
|
| ASSET | 112601 | Loans Receivable | 100.0 | |
|
||||||
| LIABILITY | 145024 | Deferred Capitalized Income | | 100.0 |
|
| LIABILITY | 145024 | Deferred Capitalized Income | | 100.0 |
|
||||||
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "2 January 2024" with 1000 EUR transaction amount and system-generated Idempotency key
|
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "2 January 2024" with 1100 EUR transaction amount and system-generated Idempotency key
|
||||||
Then Loan Transactions tab has the following data:
|
Then Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||||
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||||
| 02 January 2024 | Repayment | 1000.0 | 900.0 | 0.17 | 0.0 | 0.0 | 100.0 | false |
|
| 02 January 2024 | Repayment | 1100.0 | 1000.0 | 0.17 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 02 January 2024 | Accrual | 0.17 | 0.0 | 0.17 | 0.0 | 0.0 | 0.0 | false |
|
| 02 January 2024 | Accrual | 0.17 | 0.0 | 0.17 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
| 02 January 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
||||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "02 January 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "02 January 2024" which has the following Journal entries:
|
||||||
@@ -262,7 +262,7 @@ Feature: Capitalized Income
|
|||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||||
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||||
| 02 January 2024 | Close (as written-off) | 905.25 | 900.0 | 5.25 | 0.0 | 0.0 | 100.0 | false |
|
| 02 January 2024 | Close (as written-off) | 1005.81| 1000.0 | 5.81 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
| 02 January 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
||||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "02 January 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "02 January 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
@@ -555,16 +555,16 @@ Feature: Capitalized Income
|
|||||||
And Admin does charge-off the loan with reason "DELINQUENT" on "03 February 2024"
|
And Admin does charge-off the loan with reason "DELINQUENT" on "03 February 2024"
|
||||||
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
| ASSET | 112601 | Loans Receivable | | 900.0 |
|
| ASSET | 112601 | Loans Receivable | | 1000.0 |
|
||||||
| ASSET | 112603 | Interest/Fee Receivable | | 4.0 |
|
| ASSET | 112603 | Interest/Fee Receivable | | 4.58 |
|
||||||
| EXPENSE | 744007 | Credit Loss/Bad Debt | 900.0 | |
|
| EXPENSE | 744007 | Credit Loss/Bad Debt | 1000.0| |
|
||||||
| INCOME | 404001 | Interest Income Charge Off | 4.0 | |
|
| INCOME | 404001 | Interest Income Charge Off | 4.58 | |
|
||||||
Then Loan Transactions tab has the following data:
|
Then Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||||
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||||
| 03 February 2024 | Accrual | 4.0 | 0.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Accrual | 4.58 | 0.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Charge-off | 904.0 | 900.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Charge-off | 1004.58| 1000.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
||||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
@@ -597,16 +597,16 @@ Feature: Capitalized Income
|
|||||||
And Admin does charge-off the loan on "03 February 2024"
|
And Admin does charge-off the loan on "03 February 2024"
|
||||||
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
| ASSET | 112601 | Loans Receivable | | 900.0 |
|
| ASSET | 112601 | Loans Receivable | | 1000.0 |
|
||||||
| ASSET | 112603 | Interest/Fee Receivable | | 4.0 |
|
| ASSET | 112603 | Interest/Fee Receivable | | 4.58 |
|
||||||
| EXPENSE | 744037 | Credit Loss/Bad Debt-Fraud | 900.0 | |
|
| EXPENSE | 744037 | Credit Loss/Bad Debt-Fraud | 1000.0| |
|
||||||
| INCOME | 404001 | Interest Income Charge Off | 4.0 | |
|
| INCOME | 404001 | Interest Income Charge Off | 4.58 | |
|
||||||
Then Loan Transactions tab has the following data:
|
Then Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||||
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||||
| 03 February 2024 | Accrual | 4.0 | 0.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Accrual | 4.58 | 0.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Charge-off | 904.0 | 900.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Charge-off | 1004.58| 1000.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
||||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
@@ -639,16 +639,16 @@ Feature: Capitalized Income
|
|||||||
And Admin does charge-off the loan with reason "DELINQUENT" on "03 February 2024"
|
And Admin does charge-off the loan with reason "DELINQUENT" on "03 February 2024"
|
||||||
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
| ASSET | 112601 | Loans Receivable | | 900.0 |
|
| ASSET | 112601 | Loans Receivable | | 1000.0 |
|
||||||
| ASSET | 112603 | Interest/Fee Receivable | | 4.0 |
|
| ASSET | 112603 | Interest/Fee Receivable | | 4.58 |
|
||||||
| EXPENSE | 744007 | Credit Loss/Bad Debt | 900.0 | |
|
| EXPENSE | 744007 | Credit Loss/Bad Debt | 1000.0| |
|
||||||
| INCOME | 404001 | Interest Income Charge Off | 4.0 | |
|
| INCOME | 404001 | Interest Income Charge Off | 4.58 | |
|
||||||
Then Loan Transactions tab has the following data:
|
Then Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||||
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||||
| 03 February 2024 | Accrual | 4.0 | 0.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Accrual | 4.58 | 0.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Charge-off | 904.0 | 900.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Charge-off | 1004.58| 1000.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
||||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
@@ -681,16 +681,16 @@ Feature: Capitalized Income
|
|||||||
And Admin does charge-off the loan with reason "DELINQUENT" on "03 February 2024"
|
And Admin does charge-off the loan with reason "DELINQUENT" on "03 February 2024"
|
||||||
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
| ASSET | 112601 | Loans Receivable | | 900.0 |
|
| ASSET | 112601 | Loans Receivable | | 1000.0 |
|
||||||
| ASSET | 112603 | Interest/Fee Receivable | | 4.0 |
|
| ASSET | 112603 | Interest/Fee Receivable | | 4.58 |
|
||||||
| EXPENSE | 744007 | Credit Loss/Bad Debt | 900.0 | |
|
| EXPENSE | 744007 | Credit Loss/Bad Debt | 1000.0| |
|
||||||
| INCOME | 404001 | Interest Income Charge Off | 4.0 | |
|
| INCOME | 404001 | Interest Income Charge Off | 4.58 | |
|
||||||
Then Loan Transactions tab has the following data:
|
Then Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||||
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||||
| 03 February 2024 | Accrual | 4.0 | 0.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Accrual | 4.58 | 0.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Charge-off | 904.0 | 900.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Charge-off | 1004.58| 1000.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Capitalized Income Amortization | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | false |
|
||||||
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CAPITALIZED_INCOME_AMORTIZATION" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
@@ -700,20 +700,20 @@ Feature: Capitalized Income
|
|||||||
Then Admin does a charge-off undo the loan
|
Then Admin does a charge-off undo the loan
|
||||||
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
| ASSET | 112601 | Loans Receivable | | 900.0 |
|
| ASSET | 112601 | Loans Receivable | | 1000.0 |
|
||||||
| ASSET | 112603 | Interest/Fee Receivable | | 4.0 |
|
| ASSET | 112603 | Interest/Fee Receivable | | 4.58 |
|
||||||
| EXPENSE | 744007 | Credit Loss/Bad Debt | 900.0 | |
|
| EXPENSE | 744007 | Credit Loss/Bad Debt | 1000.0| |
|
||||||
| INCOME | 404001 | Interest Income Charge Off | 4.0 | |
|
| INCOME | 404001 | Interest Income Charge Off | 4.58 | |
|
||||||
| ASSET | 112601 | Loans Receivable | 900.0 | |
|
| ASSET | 112601 | Loans Receivable | 1000.0| |
|
||||||
| ASSET | 112603 | Interest/Fee Receivable | 4.0 | |
|
| ASSET | 112603 | Interest/Fee Receivable | 4.58 | |
|
||||||
| EXPENSE | 744007 | Credit Loss/Bad Debt | | 900.0 |
|
| EXPENSE | 744007 | Credit Loss/Bad Debt | | 1000.0 |
|
||||||
| INCOME | 404001 | Interest Income Charge Off | | 4.0 |
|
| INCOME | 404001 | Interest Income Charge Off | | 4.58 |
|
||||||
Then Loan Transactions tab has the following data:
|
Then Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||||
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
| 01 January 2024 | Disbursement | 900.0 | 0.0 | 0.0 | 0.0 | 0.0 | 900.0 | false |
|
||||||
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
| 02 January 2024 | Capitalized Income | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||||
| 03 February 2024 | Accrual | 4.0 | 0.0 | 4.0 | 0.0 | 0.0 | 0.0 | false |
|
| 03 February 2024 | Accrual | 4.58 | 0.0 | 4.58 | 0.0 | 0.0 | 0.0 | false |
|
||||||
| 03 February 2024 | Charge-off | 904.0 | 900.0 | 4.0 | 0.0 | 0.0 | 0.0 | true |
|
| 03 February 2024 | Charge-off | 1004.58| 1000.0 | 4.58 | 0.0 | 0.0 | 0.0 | true |
|
||||||
Then Reversed loan capitalized income amortization transaction has the following Journal entries:
|
Then Reversed loan capitalized income amortization transaction has the following Journal entries:
|
||||||
| Type | Account code | Account name | Debit | Credit |
|
| Type | Account code | Account name | Debit | Credit |
|
||||||
| EXPENSE | 744007 | Credit Loss/Bad Debt | | 100.0 |
|
| EXPENSE | 744007 | Credit Loss/Bad Debt | | 100.0 |
|
||||||
|
|||||||
+2
-3
@@ -30,7 +30,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul
|
|||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public final class DisbursementData implements Comparable<DisbursementData> {
|
public final class DisbursementData implements LoanPrincipalRelatedDataHolder, Comparable<DisbursementData> {
|
||||||
|
|
||||||
private final Long id;
|
private final Long id;
|
||||||
private final LocalDate expectedDisbursementDate;
|
private final LocalDate expectedDisbursementDate;
|
||||||
@@ -102,8 +102,7 @@ public final class DisbursementData implements Comparable<DisbursementData> {
|
|||||||
|
|
||||||
private boolean occursOnDayFromAndIncludingAndUpTo(final LocalDate fromInclusive, final LocalDate upToNotInclusive,
|
private boolean occursOnDayFromAndIncludingAndUpTo(final LocalDate fromInclusive, final LocalDate upToNotInclusive,
|
||||||
final LocalDate target) {
|
final LocalDate target) {
|
||||||
return (DateUtils.isEqual(target, fromInclusive) || DateUtils.isAfter(target, fromInclusive))
|
return DateUtils.isDateInRangeFromInclusiveToExclusive(fromInclusive, upToNotInclusive, target);
|
||||||
&& DateUtils.isBefore(target, upToNotInclusive);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getWaivedChargeAmount() {
|
public BigDecimal getWaivedChargeAmount() {
|
||||||
|
|||||||
+21
@@ -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.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.data;
|
||||||
|
|
||||||
|
public interface LoanPrincipalRelatedDataHolder {}
|
||||||
+2
@@ -34,6 +34,8 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
|||||||
public class LoanSummaryData {
|
public class LoanSummaryData {
|
||||||
|
|
||||||
private final CurrencyData currency;
|
private final CurrencyData currency;
|
||||||
|
private final BigDecimal totalPrincipal;
|
||||||
|
private final BigDecimal totalPrincipalFromCapitalizedIncome;
|
||||||
private final BigDecimal principalDisbursed;
|
private final BigDecimal principalDisbursed;
|
||||||
private final BigDecimal principalAdjustments;
|
private final BigDecimal principalAdjustments;
|
||||||
private final BigDecimal principalPaid;
|
private final BigDecimal principalPaid;
|
||||||
|
|||||||
+17
-4
@@ -38,6 +38,12 @@ import org.apache.fineract.organisation.monetary.domain.Money;
|
|||||||
public class LoanSummary {
|
public class LoanSummary {
|
||||||
|
|
||||||
// derived totals fields
|
// derived totals fields
|
||||||
|
@Column(name = "total_principal_derived", scale = 6, precision = 19)
|
||||||
|
private BigDecimal totalPrincipal;
|
||||||
|
|
||||||
|
@Column(name = "capitalized_income_derived", scale = 6, precision = 19)
|
||||||
|
private BigDecimal totalPrincipalFromCapitalizedIncome;
|
||||||
|
|
||||||
@Column(name = "principal_disbursed_derived", scale = 6, precision = 19)
|
@Column(name = "principal_disbursed_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal totalPrincipalDisbursed;
|
private BigDecimal totalPrincipalDisbursed;
|
||||||
|
|
||||||
@@ -166,6 +172,8 @@ public class LoanSummary {
|
|||||||
this.totalPenaltyChargesWaived = BigDecimal.ZERO;
|
this.totalPenaltyChargesWaived = BigDecimal.ZERO;
|
||||||
this.totalPenaltyChargesWrittenOff = BigDecimal.ZERO;
|
this.totalPenaltyChargesWrittenOff = BigDecimal.ZERO;
|
||||||
this.totalPrincipalAdjustments = BigDecimal.ZERO;
|
this.totalPrincipalAdjustments = BigDecimal.ZERO;
|
||||||
|
this.totalPrincipal = BigDecimal.ZERO;
|
||||||
|
this.totalPrincipalFromCapitalizedIncome = BigDecimal.ZERO;
|
||||||
this.totalPrincipalDisbursed = BigDecimal.ZERO;
|
this.totalPrincipalDisbursed = BigDecimal.ZERO;
|
||||||
this.totalPrincipalOutstanding = BigDecimal.ZERO;
|
this.totalPrincipalOutstanding = BigDecimal.ZERO;
|
||||||
this.totalPrincipalRepaid = BigDecimal.ZERO;
|
this.totalPrincipalRepaid = BigDecimal.ZERO;
|
||||||
@@ -176,17 +184,22 @@ public class LoanSummary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateSummary(final MonetaryCurrency currency, final Money principal,
|
public void updateSummary(final MonetaryCurrency currency, final Money principal,
|
||||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, Set<LoanCharge> charges) {
|
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, Set<LoanCharge> charges,
|
||||||
|
List<LoanTransaction> capitalizedIncomeTransactions) {
|
||||||
|
final BigDecimal principalFromCapitalizedIncome = capitalizedIncomeTransactions.stream().filter(LoanTransaction::isNotReversed)
|
||||||
|
.map(LoanTransaction::getPrincipalPortion).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
|
||||||
this.totalPrincipalDisbursed = principal.getAmount();
|
this.totalPrincipalDisbursed = principal.getAmount();
|
||||||
|
this.totalPrincipalFromCapitalizedIncome = principalFromCapitalizedIncome;
|
||||||
|
this.totalPrincipal = principal.plus(principalFromCapitalizedIncome).getAmount();
|
||||||
this.totalPrincipalAdjustments = calculateTotalPrincipalAdjusted(repaymentScheduleInstallments, currency).getAmount();
|
this.totalPrincipalAdjustments = calculateTotalPrincipalAdjusted(repaymentScheduleInstallments, currency).getAmount();
|
||||||
this.totalFeeAdjustments = calculateTotalFeeAdjusted(repaymentScheduleInstallments, currency).getAmount();
|
this.totalFeeAdjustments = calculateTotalFeeAdjusted(repaymentScheduleInstallments, currency).getAmount();
|
||||||
this.totalPenaltyAdjustments = calculateTotalPenaltyAdjusted(repaymentScheduleInstallments, currency).getAmount();
|
this.totalPenaltyAdjustments = calculateTotalPenaltyAdjusted(repaymentScheduleInstallments, currency).getAmount();
|
||||||
this.totalPrincipalRepaid = calculateTotalPrincipalRepaid(repaymentScheduleInstallments, currency).getAmount();
|
this.totalPrincipalRepaid = calculateTotalPrincipalRepaid(repaymentScheduleInstallments, currency).getAmount();
|
||||||
this.totalPrincipalWrittenOff = calculateTotalPrincipalWrittenOff(repaymentScheduleInstallments, currency).getAmount();
|
this.totalPrincipalWrittenOff = calculateTotalPrincipalWrittenOff(repaymentScheduleInstallments, currency).getAmount();
|
||||||
|
|
||||||
this.totalPrincipalOutstanding = principal.plus(this.totalPrincipalAdjustments).minus(this.totalPrincipalRepaid)
|
this.totalPrincipalOutstanding = principal.plus(principalFromCapitalizedIncome).plus(this.totalPrincipalAdjustments)
|
||||||
.minus(this.totalPrincipalWrittenOff).getAmount();
|
.minus(this.totalPrincipalRepaid).minus(this.totalPrincipalWrittenOff).getAmount();
|
||||||
|
|
||||||
final Money totalInterestCharged = calculateTotalInterestCharged(repaymentScheduleInstallments, currency);
|
final Money totalInterestCharged = calculateTotalInterestCharged(repaymentScheduleInstallments, currency);
|
||||||
this.totalInterestCharged = totalInterestCharged.getAmount();
|
this.totalInterestCharged = totalInterestCharged.getAmount();
|
||||||
@@ -225,7 +238,7 @@ public class LoanSummary {
|
|||||||
this.totalPenaltyChargesOutstanding = totalPenaltyChargesCharged.minus(this.totalPenaltyChargesRepaid)
|
this.totalPenaltyChargesOutstanding = totalPenaltyChargesCharged.minus(this.totalPenaltyChargesRepaid)
|
||||||
.minus(this.totalPenaltyChargesWaived).minus(this.totalPenaltyChargesWrittenOff).getAmount();
|
.minus(this.totalPenaltyChargesWaived).minus(this.totalPenaltyChargesWrittenOff).getAmount();
|
||||||
|
|
||||||
final Money totalExpectedRepayment = Money.of(currency, this.totalPrincipalDisbursed).plus(this.totalInterestCharged)
|
final Money totalExpectedRepayment = Money.of(currency, this.totalPrincipal).plus(this.totalInterestCharged)
|
||||||
.plus(this.totalFeeChargesCharged).plus(this.totalPenaltyChargesCharged);
|
.plus(this.totalFeeChargesCharged).plus(this.totalPenaltyChargesCharged);
|
||||||
this.totalExpectedRepayment = totalExpectedRepayment.getAmount();
|
this.totalExpectedRepayment = totalExpectedRepayment.getAmount();
|
||||||
|
|
||||||
|
|||||||
+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.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.LoanPrincipalRelatedDataHolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable data object representing a subset of loan transaction data.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class LoanTransactionRepaymentPeriodData implements LoanPrincipalRelatedDataHolder, Serializable {
|
||||||
|
|
||||||
|
private final Long transactionId;
|
||||||
|
private final Long loanId;
|
||||||
|
private final LocalDate date;
|
||||||
|
private final boolean reversed;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
private final BigDecimal unrecognizedAmount;
|
||||||
|
private final BigDecimal feeChargesPortion;
|
||||||
|
|
||||||
|
public LoanTransactionRepaymentPeriodData(Long transactionId, Long loanId, LocalDate date, boolean reversed, BigDecimal amount,
|
||||||
|
BigDecimal unrecognizedAmount, BigDecimal feeChargesPortion) {
|
||||||
|
this.transactionId = transactionId;
|
||||||
|
this.loanId = loanId;
|
||||||
|
this.date = date;
|
||||||
|
this.reversed = reversed;
|
||||||
|
this.amount = amount;
|
||||||
|
this.unrecognizedAmount = unrecognizedAmount;
|
||||||
|
this.feeChargesPortion = feeChargesPortion;
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-1
@@ -104,7 +104,10 @@ public final class LoanRefundValidator {
|
|||||||
if (loan.getLoanProduct().isMultiDisburseLoan() && adjustedTransaction == null) {
|
if (loan.getLoanProduct().isMultiDisburseLoan() && adjustedTransaction == null) {
|
||||||
final BigDecimal totalDisbursed = loan.getDisbursedAmount();
|
final BigDecimal totalDisbursed = loan.getDisbursedAmount();
|
||||||
final BigDecimal totalPrincipalAdjusted = loan.getSummary().getTotalPrincipalAdjustments();
|
final BigDecimal totalPrincipalAdjusted = loan.getSummary().getTotalPrincipalAdjustments();
|
||||||
final BigDecimal totalPrincipalCredited = totalDisbursed.add(totalPrincipalAdjusted);
|
final BigDecimal totalPrincipalFromCapitalizedIncome = loan.getLoanTransactions(LoanTransaction::isCapitalizedIncome).stream()
|
||||||
|
.filter(LoanTransaction::isNotReversed).map(LoanTransaction::getPrincipalPortion)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
final BigDecimal totalPrincipalCredited = totalDisbursed.add(totalPrincipalAdjusted).add(totalPrincipalFromCapitalizedIncome);
|
||||||
if (totalPrincipalCredited.compareTo(loan.getSummary().getTotalPrincipalRepaid()) < 0) {
|
if (totalPrincipalCredited.compareTo(loan.getSummary().getTotalPrincipalRepaid()) < 0) {
|
||||||
final String errorMessage = "The transaction amount cannot exceed threshold.";
|
final String errorMessage = "The transaction amount cannot exceed threshold.";
|
||||||
throw new InvalidLoanStateTransitionException("transaction", "amount.exceeds.threshold", errorMessage);
|
throw new InvalidLoanStateTransitionException("transaction", "amount.exceeds.threshold", errorMessage);
|
||||||
|
|||||||
+2
-1
@@ -97,7 +97,8 @@ public class LoanBalanceService {
|
|||||||
loan.setTotalRecovered(recoveredAmount.getAmountDefaultedToNullIfZero());
|
loan.setTotalRecovered(recoveredAmount.getAmountDefaultedToNullIfZero());
|
||||||
|
|
||||||
final Money principal = loan.getLoanRepaymentScheduleDetail().getPrincipal();
|
final Money principal = loan.getLoanRepaymentScheduleDetail().getPrincipal();
|
||||||
loan.getSummary().updateSummary(loan.getCurrency(), principal, loan.getRepaymentScheduleInstallments(), loan.getLoanCharges());
|
List<LoanTransaction> capitalizedIncomeTransactions = loan.getLoanTransactions(LoanTransaction::isCapitalizedIncome);
|
||||||
|
loan.getSummary().updateSummary(loan.getCurrency(), principal, loan.getRepaymentScheduleInstallments(), loan.getLoanCharges(), capitalizedIncomeTransactions);
|
||||||
loan.updateLoanOutstandingBalances();
|
loan.updateLoanOutstandingBalances();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -173,7 +173,10 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
|||||||
if (loan.getLoanProduct().isMultiDisburseLoan()) {
|
if (loan.getLoanProduct().isMultiDisburseLoan()) {
|
||||||
final BigDecimal totalDisbursed = loan.getDisbursedAmount();
|
final BigDecimal totalDisbursed = loan.getDisbursedAmount();
|
||||||
final BigDecimal totalPrincipalAdjusted = loan.getSummary().getTotalPrincipalAdjustments();
|
final BigDecimal totalPrincipalAdjusted = loan.getSummary().getTotalPrincipalAdjustments();
|
||||||
final BigDecimal totalPrincipalCredited = totalDisbursed.add(totalPrincipalAdjusted);
|
final BigDecimal totalPrincipalFromCapitalizedIncome = loan.getLoanTransactions(LoanTransaction::isCapitalizedIncome).stream()
|
||||||
|
.filter(LoanTransaction::isNotReversed).map(LoanTransaction::getPrincipalPortion)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
final BigDecimal totalPrincipalCredited = totalDisbursed.add(totalPrincipalAdjusted).add(totalPrincipalFromCapitalizedIncome);
|
||||||
if (totalPrincipalCredited.compareTo(loan.getSummary().getTotalPrincipalRepaid()) < 0
|
if (totalPrincipalCredited.compareTo(loan.getSummary().getTotalPrincipalRepaid()) < 0
|
||||||
&& loan.repaymentScheduleDetail().getPrincipal().minus(totalDisbursed).isGreaterThanZero()) {
|
&& loan.repaymentScheduleDetail().getPrincipal().minus(totalDisbursed).isGreaterThanZero()) {
|
||||||
final String errorMessage = "The transaction amount cannot exceed threshold.";
|
final String errorMessage = "The transaction amount cannot exceed threshold.";
|
||||||
|
|||||||
+3
-1
@@ -37,6 +37,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
|
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
|
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||||
@@ -50,7 +51,8 @@ public interface LoanReadPlatformService {
|
|||||||
LoanAccountData fetchRepaymentScheduleData(LoanAccountData accountData);
|
LoanAccountData fetchRepaymentScheduleData(LoanAccountData accountData);
|
||||||
|
|
||||||
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData,
|
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData,
|
||||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType);
|
Collection<DisbursementData> disbursementData, Collection<LoanTransactionRepaymentPeriodData> capitalizedIncomeData,
|
||||||
|
boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType);
|
||||||
|
|
||||||
Collection<LoanTransactionData> retrieveLoanTransactions(Long loanId);
|
Collection<LoanTransactionData> retrieveLoanTransactions(Long loanId);
|
||||||
|
|
||||||
|
|||||||
+71
-18
@@ -344,6 +344,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
case REAMORTIZE -> handleReAmortization(loanTransaction, ctx);
|
case REAMORTIZE -> handleReAmortization(loanTransaction, ctx);
|
||||||
case REAGE -> handleReAge(loanTransaction, ctx);
|
case REAGE -> handleReAge(loanTransaction, ctx);
|
||||||
case ACCRUAL_ACTIVITY -> calculateAccrualActivity(loanTransaction, ctx);
|
case ACCRUAL_ACTIVITY -> calculateAccrualActivity(loanTransaction, ctx);
|
||||||
|
case CAPITALIZED_INCOME -> handleCapitalizedIncome(loanTransaction, ctx);
|
||||||
// TODO: Cover rest of the transaction types
|
// TODO: Cover rest of the transaction types
|
||||||
default -> log.warn("Unhandled transaction processing for transaction type: {}", loanTransaction.getTypeOf());
|
default -> log.warn("Unhandled transaction processing for transaction type: {}", loanTransaction.getTypeOf());
|
||||||
}
|
}
|
||||||
@@ -1199,18 +1200,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
emiCalculator.addDisbursement(model, transactionDate, amortizableAmount);
|
emiCalculator.addDisbursement(model, transactionDate, amortizableAmount);
|
||||||
|
|
||||||
disbursementTransaction.resetDerivedComponents();
|
disbursementTransaction.resetDerivedComponents();
|
||||||
if (amortizableAmount.isGreaterThanZero()) {
|
recalculateRepaymentPeriodsWithEMICalculation(amortizableAmount, model, installments, transactionDate, currency);
|
||||||
model.repaymentPeriods().forEach(rm -> {
|
|
||||||
LoanRepaymentScheduleInstallment installment = installments.stream().filter(
|
|
||||||
ri -> ri.getDueDate().equals(rm.getDueDate()) && !ri.isDownPayment() && !ri.getDueDate().isBefore(transactionDate))
|
|
||||||
.findFirst().orElse(null);
|
|
||||||
if (installment != null) {
|
|
||||||
installment.updatePrincipal(rm.getDuePrincipal().getAmount());
|
|
||||||
installment.updateInterestCharged(rm.getDueInterest().getAmount());
|
|
||||||
installment.updateObligationsMet(currency, transactionDate);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
allocateOverpayment(disbursementTransaction, transactionCtx);
|
allocateOverpayment(disbursementTransaction, transactionCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1222,7 +1212,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments = installments.stream().filter(
|
List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments = installments.stream().filter(
|
||||||
i -> i.getDueDate().isAfter(disbursementTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional())
|
i -> i.getDueDate().isAfter(disbursementTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional())
|
||||||
.toList();
|
.toList();
|
||||||
int noCandidateRepaymentInstallments = candidateRepaymentInstallments.size();
|
|
||||||
LoanProductRelatedDetail loanProductRelatedDetail = disbursementTransaction.getLoan().getLoanRepaymentScheduleDetail();
|
LoanProductRelatedDetail loanProductRelatedDetail = disbursementTransaction.getLoan().getLoanRepaymentScheduleDetail();
|
||||||
Integer installmentAmountInMultiplesOf = disbursementTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf();
|
Integer installmentAmountInMultiplesOf = disbursementTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf();
|
||||||
Money downPaymentAmount = Money.zero(currency);
|
Money downPaymentAmount = Money.zero(currency);
|
||||||
@@ -1240,7 +1229,73 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
|
|
||||||
Money amortizableAmount = disbursementTransaction.getAmount(currency).minus(downPaymentAmount);
|
Money amortizableAmount = disbursementTransaction.getAmount(currency).minus(downPaymentAmount);
|
||||||
|
|
||||||
|
recalculateRepaymentInstallmentsWithoutEMICalculation(disbursementTransaction, amortizableAmount, candidateRepaymentInstallments,
|
||||||
|
currency, installmentAmountInMultiplesOf);
|
||||||
|
allocateOverpayment(disbursementTransaction, transactionCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCapitalizedIncome(LoanTransaction capitalizedIncomeTransaction, TransactionCtx transactionCtx) {
|
||||||
|
if (capitalizedIncomeTransaction.getLoan().isInterestBearing()) {
|
||||||
|
handleCapitalizedIncomeWithEMICalculator(capitalizedIncomeTransaction, transactionCtx);
|
||||||
|
} else {
|
||||||
|
handleCapitalizedIncomeWithoutEMICalculator(capitalizedIncomeTransaction, transactionCtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCapitalizedIncomeWithEMICalculator(LoanTransaction capitalizedIncomeTransaction, TransactionCtx transactionCtx) {
|
||||||
|
ProgressiveLoanInterestScheduleModel model;
|
||||||
|
if (!(transactionCtx instanceof ProgressiveTransactionCtx)
|
||||||
|
|| (model = ((ProgressiveTransactionCtx) transactionCtx).getModel()) == null) {
|
||||||
|
throw new IllegalStateException("TransactionCtx has no model");
|
||||||
|
}
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments = transactionCtx.getInstallments();
|
||||||
|
LocalDate transactionDate = capitalizedIncomeTransaction.getTransactionDate();
|
||||||
|
MonetaryCurrency currency = transactionCtx.getCurrency();
|
||||||
|
|
||||||
|
Money amortizableAmount = capitalizedIncomeTransaction.getAmount(currency);
|
||||||
|
emiCalculator.addCapitalizedIncome(model, transactionDate, amortizableAmount);
|
||||||
|
|
||||||
|
recalculateRepaymentPeriodsWithEMICalculation(amortizableAmount, model, installments, transactionDate, currency);
|
||||||
|
allocateOverpayment(capitalizedIncomeTransaction, transactionCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCapitalizedIncomeWithoutEMICalculator(LoanTransaction capitalizedIncomeTransaction, TransactionCtx transactionCtx) {
|
||||||
|
MonetaryCurrency currency = transactionCtx.getCurrency();
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments = transactionCtx.getInstallments();
|
||||||
|
List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments = installments.stream().filter(
|
||||||
|
i -> i.getDueDate().isAfter(capitalizedIncomeTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional())
|
||||||
|
.toList();
|
||||||
|
Integer installmentAmountInMultiplesOf = capitalizedIncomeTransaction.getLoan().getLoanProduct()
|
||||||
|
.getInstallmentAmountInMultiplesOf();
|
||||||
|
|
||||||
|
Money amortizableAmount = capitalizedIncomeTransaction.getAmount(currency);
|
||||||
|
|
||||||
|
recalculateRepaymentInstallmentsWithoutEMICalculation(capitalizedIncomeTransaction, amortizableAmount,
|
||||||
|
candidateRepaymentInstallments, currency, installmentAmountInMultiplesOf);
|
||||||
|
allocateOverpayment(capitalizedIncomeTransaction, transactionCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateRepaymentPeriodsWithEMICalculation(Money amortizableAmount, ProgressiveLoanInterestScheduleModel model,
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments, LocalDate transactionDate, MonetaryCurrency currency) {
|
||||||
if (amortizableAmount.isGreaterThanZero()) {
|
if (amortizableAmount.isGreaterThanZero()) {
|
||||||
|
model.repaymentPeriods().forEach(rm -> {
|
||||||
|
LoanRepaymentScheduleInstallment installment = installments.stream().filter(
|
||||||
|
ri -> ri.getDueDate().equals(rm.getDueDate()) && !ri.isDownPayment() && !ri.getDueDate().isBefore(transactionDate))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
if (installment != null) {
|
||||||
|
installment.updatePrincipal(rm.getDuePrincipal().getAmount());
|
||||||
|
installment.updateInterestCharged(rm.getDueInterest().getAmount());
|
||||||
|
installment.updateObligationsMet(currency, transactionDate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateRepaymentInstallmentsWithoutEMICalculation(LoanTransaction loanTransaction, Money amortizableAmount,
|
||||||
|
List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments, MonetaryCurrency currency,
|
||||||
|
Integer installmentAmountInMultiplesOf) {
|
||||||
|
if (amortizableAmount.isGreaterThanZero()) {
|
||||||
|
int noCandidateRepaymentInstallments = candidateRepaymentInstallments.size();
|
||||||
Money increasePrincipalBy = amortizableAmount.dividedBy(noCandidateRepaymentInstallments, MoneyHelper.getMathContext());
|
Money increasePrincipalBy = amortizableAmount.dividedBy(noCandidateRepaymentInstallments, MoneyHelper.getMathContext());
|
||||||
MoneyHolder moneyHolder = new MoneyHolder(amortizableAmount);
|
MoneyHolder moneyHolder = new MoneyHolder(amortizableAmount);
|
||||||
|
|
||||||
@@ -1252,14 +1307,12 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
i.updatePrincipal(newPrincipal.getAmount());
|
i.updatePrincipal(newPrincipal.getAmount());
|
||||||
moneyHolder.setMoneyObject(moneyHolder.getMoneyObject().minus(newPrincipal).plus(previousPrincipal));
|
moneyHolder.setMoneyObject(moneyHolder.getMoneyObject().minus(newPrincipal).plus(previousPrincipal));
|
||||||
i.updateObligationsMet(currency, disbursementTransaction.getTransactionDate());
|
i.updateObligationsMet(currency, loanTransaction.getTransactionDate());
|
||||||
});
|
});
|
||||||
// Hence the rounding, we might need to amend the last installment amount
|
// Hence the rounding, we might need to amend the last installment amount
|
||||||
candidateRepaymentInstallments.get(noCandidateRepaymentInstallments - 1)
|
candidateRepaymentInstallments.get(noCandidateRepaymentInstallments - 1).addToPrincipal(loanTransaction.getTransactionDate(),
|
||||||
.addToPrincipal(disbursementTransaction.getTransactionDate(), moneyHolder.getMoneyObject());
|
moneyHolder.getMoneyObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
allocateOverpayment(disbursementTransaction, transactionCtx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void allocateOverpayment(LoanTransaction loanTransaction, TransactionCtx transactionCtx) {
|
private void allocateOverpayment(LoanTransaction loanTransaction, TransactionCtx transactionCtx) {
|
||||||
|
|||||||
+7
@@ -20,13 +20,20 @@ package org.apache.fineract.portfolio.loanaccount.repository;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
|
||||||
public interface LoanCapitalizedIncomeBalanceRepository
|
public interface LoanCapitalizedIncomeBalanceRepository
|
||||||
extends JpaRepository<LoanCapitalizedIncomeBalance, Long>, JpaSpecificationExecutor<LoanCapitalizedIncomeBalance> {
|
extends JpaRepository<LoanCapitalizedIncomeBalance, Long>, JpaSpecificationExecutor<LoanCapitalizedIncomeBalance> {
|
||||||
|
|
||||||
|
String FIND_BALANCE_REPAYMENT_SCHEDULE_DATA = "SELECT new org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData(lcib.loanTransaction.id, lcib.loan.id, lcib.loanTransaction.dateOf, lcib.loanTransaction.reversed, lcib.amount, lcib.unrecognizedAmount, lcib.loanTransaction.feeChargesPortion) FROM LoanCapitalizedIncomeBalance lcib ";
|
||||||
|
|
||||||
List<LoanCapitalizedIncomeBalance> findAllByLoanId(Long loanId);
|
List<LoanCapitalizedIncomeBalance> findAllByLoanId(Long loanId);
|
||||||
|
|
||||||
LoanCapitalizedIncomeBalance findByLoanIdAndLoanTransactionId(Long loanId, Long transactionId);
|
LoanCapitalizedIncomeBalance findByLoanIdAndLoanTransactionId(Long loanId, Long transactionId);
|
||||||
|
|
||||||
|
@Query(FIND_BALANCE_REPAYMENT_SCHEDULE_DATA + " WHERE lcib.loan.id = :loanId")
|
||||||
|
List<LoanTransactionRepaymentPeriodData> findRepaymentPeriodDataByLoanId(Long loanId);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-2
@@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
|||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
@@ -54,6 +55,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
private final NoteWritePlatformService noteWritePlatformService;
|
private final NoteWritePlatformService noteWritePlatformService;
|
||||||
private final ExternalIdFactory externalIdFactory;
|
private final ExternalIdFactory externalIdFactory;
|
||||||
private final LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository;
|
private final LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository;
|
||||||
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
@@ -74,9 +76,10 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
final Money capitalizedIncomeAmount = calculateCapitalizedIncomeAmount(loan, transactionAmount);
|
final Money capitalizedIncomeAmount = calculateCapitalizedIncomeAmount(loan, transactionAmount);
|
||||||
final LoanTransaction capitalizedIncomeTransaction = LoanTransaction.capitalizedIncome(loan, capitalizedIncomeAmount, paymentDetail,
|
final LoanTransaction capitalizedIncomeTransaction = LoanTransaction.capitalizedIncome(loan, capitalizedIncomeAmount, paymentDetail,
|
||||||
transactionDate, txnExternalId);
|
transactionDate, txnExternalId);
|
||||||
// TODO: Transaction reverse-replay + loan repayment schedule update (follow-up story)
|
|
||||||
// Update loan with capitalized income
|
// Update loan with capitalized income
|
||||||
loan.addLoanTransaction(capitalizedIncomeTransaction);
|
loan.addLoanTransaction(capitalizedIncomeTransaction);
|
||||||
|
// Recalculate loan transactions
|
||||||
|
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeTransaction);
|
||||||
// Create capitalized income balances
|
// Create capitalized income balances
|
||||||
createCapitalizedIncomeBalance(capitalizedIncomeTransaction);
|
createCapitalizedIncomeBalance(capitalizedIncomeTransaction);
|
||||||
// Save and flush (PK is set)
|
// Save and flush (PK is set)
|
||||||
@@ -88,7 +91,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
// Create a note if provided
|
// Create a note if provided
|
||||||
final String noteText = command.stringValueOfParameterNamed("note");
|
final String noteText = command.stringValueOfParameterNamed("note");
|
||||||
if (noteText != null && !noteText.isEmpty()) {
|
if (noteText != null && !noteText.isEmpty()) {
|
||||||
noteWritePlatformService.createLoanNote(loanId, noteText);
|
noteWritePlatformService.createLoanTransactionNote(capitalizedIncomeTransaction.getId(), noteText);
|
||||||
}
|
}
|
||||||
// Post journal entries
|
// Post journal entries
|
||||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
@@ -99,6 +102,14 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction capitalizedIncomeTransaction) {
|
||||||
|
if (loan.isInterestBearingAndInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transactionDate)) {
|
||||||
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
|
} else {
|
||||||
|
reprocessLoanTransactionsService.processLatestTransaction(capitalizedIncomeTransaction, loan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetBalance(final Long loanId) {
|
public void resetBalance(final Long loanId) {
|
||||||
capitalizedIncomeBalanceRepository
|
capitalizedIncomeBalanceRepository
|
||||||
|
|||||||
+4
-2
@@ -30,6 +30,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidator;
|
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidatorImpl;
|
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidatorImpl;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
|
import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
@@ -45,10 +46,11 @@ public class ProgressiveLoanAccountConfiguration {
|
|||||||
LoanAssembler loanAssembler, LoanTransactionRepository loanTransactionRepository,
|
LoanAssembler loanAssembler, LoanTransactionRepository loanTransactionRepository,
|
||||||
PaymentDetailWritePlatformService paymentDetailWritePlatformService, LoanJournalEntryPoster journalEntryPoster,
|
PaymentDetailWritePlatformService paymentDetailWritePlatformService, LoanJournalEntryPoster journalEntryPoster,
|
||||||
NoteWritePlatformService noteWritePlatformService, ExternalIdFactory externalIdFactory,
|
NoteWritePlatformService noteWritePlatformService, ExternalIdFactory externalIdFactory,
|
||||||
LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository) {
|
LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
|
||||||
|
ReprocessLoanTransactionsService reprocessLoanTransactionsService) {
|
||||||
return new CapitalizedIncomeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
|
return new CapitalizedIncomeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
|
||||||
paymentDetailWritePlatformService, journalEntryPoster, noteWritePlatformService, externalIdFactory,
|
paymentDetailWritePlatformService, journalEntryPoster, noteWritePlatformService, externalIdFactory,
|
||||||
capitalizedIncomeBalanceRepository);
|
capitalizedIncomeBalanceRepository, reprocessLoanTransactionsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
+6
@@ -65,6 +65,12 @@ public interface EMICalculator {
|
|||||||
*/
|
*/
|
||||||
void addDisbursement(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate disbursementDueDate, Money disbursedAmount);
|
void addDisbursement(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate disbursementDueDate, Money disbursedAmount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the capitalized income transaction on the interest model. This method recalculates the EMI amounts from
|
||||||
|
* the action date.
|
||||||
|
*/
|
||||||
|
void addCapitalizedIncome(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDueDate, Money transactionAmount);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the interest rate change on the interest model. This method recalculates the EMI amounts from the action
|
* Applies the interest rate change on the interest model. This method recalculates the EMI amounts from the action
|
||||||
* date.
|
* date.
|
||||||
|
|||||||
+29
-6
@@ -119,7 +119,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
private void addDisbursement(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
|
private void addDisbursement(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
|
||||||
scheduleModel
|
scheduleModel
|
||||||
.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), operation.getAmount(),
|
.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), operation.getAmount(),
|
||||||
scheduleModel.zero())
|
scheduleModel.zero(), scheduleModel.zero())
|
||||||
.ifPresent((repaymentPeriod) -> calculateEMIValueAndRateFactors(
|
.ifPresent((repaymentPeriod) -> calculateEMIValueAndRateFactors(
|
||||||
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, operation.getSubmittedOnDate()), scheduleModel,
|
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, operation.getSubmittedOnDate()), scheduleModel,
|
||||||
operation));
|
operation));
|
||||||
@@ -140,6 +140,24 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
return changedRepaymentPeriod.getDueDate();
|
return changedRepaymentPeriod.getDueDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add capitalized income to Interest Period
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addCapitalizedIncome(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate transactionDueDate,
|
||||||
|
final Money transactionAmount) {
|
||||||
|
addCapitalizedIncome(scheduleModel, EmiChangeOperation.capitalizedIncome(transactionDueDate, transactionAmount));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCapitalizedIncome(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
|
||||||
|
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), scheduleModel.zero(),
|
||||||
|
scheduleModel.zero(), operation.getAmount()).ifPresent((repaymentPeriod) -> {
|
||||||
|
calculateEMIValueAndRateFactors(
|
||||||
|
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, operation.getSubmittedOnDate()), scheduleModel,
|
||||||
|
operation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changeInterestRate(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate newInterestSubmittedOnDate,
|
public void changeInterestRate(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate newInterestSubmittedOnDate,
|
||||||
final BigDecimal newInterestRate) {
|
final BigDecimal newInterestRate) {
|
||||||
@@ -151,7 +169,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
scheduleModel.addInterestRate(interestRateChangeEffectiveDate, operation.getInterestRate());
|
scheduleModel.addInterestRate(interestRateChangeEffectiveDate, operation.getInterestRate());
|
||||||
scheduleModel
|
scheduleModel
|
||||||
.changeOutstandingBalanceAndUpdateInterestPeriods(interestRateChangeEffectiveDate, scheduleModel.zero(),
|
.changeOutstandingBalanceAndUpdateInterestPeriods(interestRateChangeEffectiveDate, scheduleModel.zero(),
|
||||||
scheduleModel.zero())
|
scheduleModel.zero(), scheduleModel.zero())
|
||||||
.ifPresent(repaymentPeriod -> calculateEMIValueAndRateFactors(
|
.ifPresent(repaymentPeriod -> calculateEMIValueAndRateFactors(
|
||||||
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, interestRateChangeEffectiveDate), scheduleModel,
|
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, interestRateChangeEffectiveDate), scheduleModel,
|
||||||
operation));
|
operation));
|
||||||
@@ -160,8 +178,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
@Override
|
@Override
|
||||||
public void addBalanceCorrection(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate balanceCorrectionDate,
|
public void addBalanceCorrection(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate balanceCorrectionDate,
|
||||||
Money balanceCorrectionAmount) {
|
Money balanceCorrectionAmount) {
|
||||||
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(balanceCorrectionDate, scheduleModel.zero(), balanceCorrectionAmount)
|
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(balanceCorrectionDate, scheduleModel.zero(), balanceCorrectionAmount,
|
||||||
.ifPresent(repaymentPeriod -> {
|
scheduleModel.zero()).ifPresent(repaymentPeriod -> {
|
||||||
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
||||||
calculateOutstandingBalance(scheduleModel);
|
calculateOutstandingBalance(scheduleModel);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
||||||
@@ -237,7 +255,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
|
|
||||||
private void addCredit(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate, Money creditedPrincipalAmount,
|
private void addCredit(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate, Money creditedPrincipalAmount,
|
||||||
Money creditedInterestAmount) {
|
Money creditedInterestAmount) {
|
||||||
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(transactionDate, scheduleModel.zero(), creditedPrincipalAmount)
|
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(transactionDate, scheduleModel.zero(), creditedPrincipalAmount, scheduleModel.zero())
|
||||||
.ifPresent(repaymentPeriod -> {
|
.ifPresent(repaymentPeriod -> {
|
||||||
addCreditedAmountsToInterestPeriod(scheduleModel, transactionDate, creditedPrincipalAmount, creditedInterestAmount);
|
addCreditedAmountsToInterestPeriod(scheduleModel, transactionDate, creditedPrincipalAmount, creditedInterestAmount);
|
||||||
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
||||||
@@ -442,8 +460,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount))
|
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount))
|
||||||
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)) // 100
|
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)) // 100
|
||||||
.plus(scheduleModel.getTotalCreditedPrincipal(), mc); //
|
.plus(scheduleModel.getTotalCreditedPrincipal(), mc); //
|
||||||
|
Money totalCapitalizedIncome = scheduleModel.repaymentPeriods().stream()
|
||||||
|
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getCapitalizedIncomePrincipal))
|
||||||
|
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 100
|
||||||
|
|
||||||
Money diff = totalDisbursedAmount.plus(totalDueInterest, mc).minus(totalEMI, mc);
|
Money diff = totalDisbursedAmount.plus(totalCapitalizedIncome, mc).plus(totalDueInterest, mc).minus(totalEMI, mc);
|
||||||
Optional<RepaymentPeriod> findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid())
|
Optional<RepaymentPeriod> findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid())
|
||||||
.reduce((first, second) -> second);
|
.reduce((first, second) -> second);
|
||||||
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> {
|
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> {
|
||||||
@@ -809,7 +830,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.copyWithoutPaidAmounts();
|
final ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.copyWithoutPaidAmounts();
|
||||||
|
|
||||||
addDisbursement(scheduleModelCopy, operation.withZeroAmount());
|
addDisbursement(scheduleModelCopy, operation.withZeroAmount());
|
||||||
|
addCapitalizedIncome(scheduleModelCopy, operation.withZeroAmount());
|
||||||
|
|
||||||
final LocalDate firstDueDate = repaymentPeriods.get(0).getDueDate();
|
final LocalDate firstDueDate = repaymentPeriods.get(0).getDueDate();
|
||||||
scheduleModel.copyPeriodsFrom(firstDueDate, scheduleModelCopy.repaymentPeriods(), (newRepaymentPeriod, actualRepaymentPeriod) -> {
|
scheduleModel.copyPeriodsFrom(firstDueDate, scheduleModelCopy.repaymentPeriods(), (newRepaymentPeriod, actualRepaymentPeriod) -> {
|
||||||
|
|||||||
+7
-2
@@ -31,7 +31,8 @@ public class EmiChangeOperation {
|
|||||||
|
|
||||||
public enum Action { //
|
public enum Action { //
|
||||||
DISBURSEMENT, //
|
DISBURSEMENT, //
|
||||||
INTEREST_RATE_CHANGE //
|
INTEREST_RATE_CHANGE, //
|
||||||
|
CAPITALIZED_INCOME //
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Action action;
|
private final Action action;
|
||||||
@@ -48,8 +49,12 @@ public class EmiChangeOperation {
|
|||||||
return new EmiChangeOperation(EmiChangeOperation.Action.INTEREST_RATE_CHANGE, newInterestSubmittedOnDate, null, newInterestRate);
|
return new EmiChangeOperation(EmiChangeOperation.Action.INTEREST_RATE_CHANGE, newInterestSubmittedOnDate, null, newInterestRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EmiChangeOperation capitalizedIncome(final LocalDate transactionDueDate, final Money transactionAmount) {
|
||||||
|
return new EmiChangeOperation(Action.CAPITALIZED_INCOME, transactionDueDate, transactionAmount, null);
|
||||||
|
}
|
||||||
|
|
||||||
public EmiChangeOperation withZeroAmount() {
|
public EmiChangeOperation withZeroAmount() {
|
||||||
if (action == Action.DISBURSEMENT) {
|
if (action == Action.DISBURSEMENT || action == Action.CAPITALIZED_INCOME) {
|
||||||
return new EmiChangeOperation(action, submittedOnDate, amount.zero(), null);
|
return new EmiChangeOperation(action, submittedOnDate, amount.zero(), null);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+15
-7
@@ -62,6 +62,8 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
private Money balanceCorrectionAmount;
|
private Money balanceCorrectionAmount;
|
||||||
private Money outstandingLoanBalance;
|
private Money outstandingLoanBalance;
|
||||||
|
|
||||||
|
private Money capitalizedIncomePrincipal;
|
||||||
|
|
||||||
@JsonExclude
|
@JsonExclude
|
||||||
private final MathContext mc;
|
private final MathContext mc;
|
||||||
private final boolean isPaused;
|
private final boolean isPaused;
|
||||||
@@ -70,38 +72,38 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
return new InterestPeriod(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(),
|
return new InterestPeriod(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(),
|
||||||
interestPeriod.getRateFactor(), interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getCreditedPrincipal(),
|
interestPeriod.getRateFactor(), interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getCreditedPrincipal(),
|
||||||
interestPeriod.getCreditedInterest(), interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
interestPeriod.getCreditedInterest(), interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
||||||
interestPeriod.getOutstandingLoanBalance(), mc, interestPeriod.isPaused());
|
interestPeriod.getOutstandingLoanBalance(), interestPeriod.getCapitalizedIncomePrincipal(), mc, interestPeriod.isPaused());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InterestPeriod empty(@NotNull RepaymentPeriod repaymentPeriod, MathContext mc) {
|
public static InterestPeriod empty(@NotNull RepaymentPeriod repaymentPeriod, MathContext mc) {
|
||||||
return new InterestPeriod(repaymentPeriod, null, null, null, null, null, null, null, null, null, mc, false);
|
return new InterestPeriod(repaymentPeriod, null, null, null, null, null, null, null, null, null, null, mc, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InterestPeriod copy(@NotNull RepaymentPeriod repaymentPeriod, @NotNull InterestPeriod interestPeriod) {
|
public static InterestPeriod copy(@NotNull RepaymentPeriod repaymentPeriod, @NotNull InterestPeriod interestPeriod) {
|
||||||
return new InterestPeriod(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(),
|
return new InterestPeriod(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(),
|
||||||
interestPeriod.getRateFactor(), interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getCreditedPrincipal(),
|
interestPeriod.getRateFactor(), interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getCreditedPrincipal(),
|
||||||
interestPeriod.getCreditedInterest(), interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
interestPeriod.getCreditedInterest(), interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
||||||
interestPeriod.getOutstandingLoanBalance(), interestPeriod.getMc(), interestPeriod.isPaused());
|
interestPeriod.getOutstandingLoanBalance(), interestPeriod.getCapitalizedIncomePrincipal(), interestPeriod.getMc(), interestPeriod.isPaused());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InterestPeriod withEmptyAmounts(@NotNull RepaymentPeriod repaymentPeriod, @NotNull LocalDate fromDate,
|
public static InterestPeriod withEmptyAmounts(@NotNull RepaymentPeriod repaymentPeriod, @NotNull LocalDate fromDate,
|
||||||
LocalDate dueDate) {
|
LocalDate dueDate) {
|
||||||
final Money zero = repaymentPeriod.getEmi().zero();
|
final Money zero = repaymentPeriod.getEmi().zero();
|
||||||
return new InterestPeriod(repaymentPeriod, fromDate, dueDate, BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, zero, zero,
|
return new InterestPeriod(repaymentPeriod, fromDate, dueDate, BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, zero, zero, zero,
|
||||||
zero.getMc(), false);
|
zero.getMc(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InterestPeriod withEmptyAmounts(@NotNull RepaymentPeriod repaymentPeriod, @NotNull LocalDate fromDate, LocalDate dueDate,
|
public static InterestPeriod withEmptyAmounts(@NotNull RepaymentPeriod repaymentPeriod, @NotNull LocalDate fromDate, LocalDate dueDate,
|
||||||
boolean isPaused) {
|
boolean isPaused) {
|
||||||
final Money zero = repaymentPeriod.getEmi().zero();
|
final Money zero = repaymentPeriod.getEmi().zero();
|
||||||
return new InterestPeriod(repaymentPeriod, fromDate, dueDate, BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, zero, zero,
|
return new InterestPeriod(repaymentPeriod, fromDate, dueDate, BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, zero, zero, zero,
|
||||||
zero.getMc(), isPaused);
|
zero.getMc(), isPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InterestPeriod withPausedAndEmptyAmounts(@NotNull RepaymentPeriod repaymentPeriod, @NotNull LocalDate fromDate,
|
public static InterestPeriod withPausedAndEmptyAmounts(@NotNull RepaymentPeriod repaymentPeriod, @NotNull LocalDate fromDate,
|
||||||
LocalDate dueDate) {
|
LocalDate dueDate) {
|
||||||
final Money zero = repaymentPeriod.getEmi().zero();
|
final Money zero = repaymentPeriod.getEmi().zero();
|
||||||
return new InterestPeriod(repaymentPeriod, fromDate, dueDate, BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, zero, zero,
|
return new InterestPeriod(repaymentPeriod, fromDate, dueDate, BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, zero, zero, zero,
|
||||||
zero.getMc(), true);
|
zero.getMc(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +128,10 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
this.creditedInterest = MathUtil.plus(this.creditedInterest, creditedInterest, mc);
|
this.creditedInterest = MathUtil.plus(this.creditedInterest, creditedInterest, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addCapitalizedIncomePrincipalAmount(final Money capitalizedIncomePrincipal) {
|
||||||
|
this.capitalizedIncomePrincipal = MathUtil.plus(this.capitalizedIncomePrincipal, capitalizedIncomePrincipal, mc);
|
||||||
|
}
|
||||||
|
|
||||||
public BigDecimal getCalculatedDueInterest() {
|
public BigDecimal getCalculatedDueInterest() {
|
||||||
if (isPaused) {
|
if (isPaused) {
|
||||||
return creditedInterest.getAmount();
|
return creditedInterest.getAmount();
|
||||||
@@ -156,6 +162,7 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
InterestPeriod previousInterestPeriod = previousRepaymentPeriod.get().getLastInterestPeriod();
|
InterestPeriod previousInterestPeriod = previousRepaymentPeriod.get().getLastInterestPeriod();
|
||||||
this.outstandingLoanBalance = MathUtil.negativeToZero(previousInterestPeriod.getOutstandingLoanBalance()//
|
this.outstandingLoanBalance = MathUtil.negativeToZero(previousInterestPeriod.getOutstandingLoanBalance()//
|
||||||
.plus(previousInterestPeriod.getDisbursementAmount(), mc)//
|
.plus(previousInterestPeriod.getDisbursementAmount(), mc)//
|
||||||
|
.plus(previousInterestPeriod.getCapitalizedIncomePrincipal(), mc)//
|
||||||
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc)//
|
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc)//
|
||||||
.minus(previousRepaymentPeriod.get().getDuePrincipal(), mc)//
|
.minus(previousRepaymentPeriod.get().getDuePrincipal(), mc)//
|
||||||
.plus(previousRepaymentPeriod.get().getPaidPrincipal(), mc), mc);//
|
.plus(previousRepaymentPeriod.get().getPaidPrincipal(), mc), mc);//
|
||||||
@@ -165,6 +172,7 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
InterestPeriod previousInterestPeriod = getRepaymentPeriod().getInterestPeriods().get(index - 1);
|
InterestPeriod previousInterestPeriod = getRepaymentPeriod().getInterestPeriods().get(index - 1);
|
||||||
this.outstandingLoanBalance = MathUtil.negativeToZero(previousInterestPeriod.getOutstandingLoanBalance() //
|
this.outstandingLoanBalance = MathUtil.negativeToZero(previousInterestPeriod.getOutstandingLoanBalance() //
|
||||||
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc) //
|
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc) //
|
||||||
|
.plus(previousInterestPeriod.getCapitalizedIncomePrincipal(), mc) //
|
||||||
.plus(previousInterestPeriod.getDisbursementAmount(), mc)); //
|
.plus(previousInterestPeriod.getDisbursementAmount(), mc)); //
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +181,7 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
* Include principal like amounts (all disbursement amount + credited principal)
|
* Include principal like amounts (all disbursement amount + credited principal)
|
||||||
*/
|
*/
|
||||||
public Money getCreditedAmounts() {
|
public Money getCreditedAmounts() {
|
||||||
return getDisbursementAmount().plus(getCreditedPrincipal(), mc);
|
return getDisbursementAmount().plus(getCreditedPrincipal(), mc).plus(getCapitalizedIncomePrincipal(), mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFirstInterestPeriod() {
|
public boolean isFirstInterestPeriod() {
|
||||||
|
|||||||
+8
-5
@@ -175,9 +175,10 @@ public class ProgressiveLoanInterestScheduleModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Optional<RepaymentPeriod> changeOutstandingBalanceAndUpdateInterestPeriods(final LocalDate balanceChangeDate,
|
public Optional<RepaymentPeriod> changeOutstandingBalanceAndUpdateInterestPeriods(final LocalDate balanceChangeDate,
|
||||||
final Money disbursedAmount, final Money correctionAmount) {
|
final Money disbursedAmount, final Money correctionAmount, final Money capitalizedIncomePrincipal) {
|
||||||
return findRepaymentPeriodForBalanceChange(balanceChangeDate).stream()//
|
return findRepaymentPeriodForBalanceChange(balanceChangeDate).stream()//
|
||||||
.peek(updateInterestPeriodOnRepaymentPeriod(balanceChangeDate, disbursedAmount, correctionAmount))//
|
.peek(updateInterestPeriodOnRepaymentPeriod(balanceChangeDate, disbursedAmount, correctionAmount,
|
||||||
|
capitalizedIncomePrincipal))//
|
||||||
.findFirst();//
|
.findFirst();//
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +215,7 @@ public class ProgressiveLoanInterestScheduleModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Consumer<RepaymentPeriod> updateInterestPeriodOnRepaymentPeriod(final LocalDate balanceChangeDate, final Money disbursedAmount,
|
private Consumer<RepaymentPeriod> updateInterestPeriodOnRepaymentPeriod(final LocalDate balanceChangeDate, final Money disbursedAmount,
|
||||||
final Money correctionAmount) {
|
final Money correctionAmount, final Money capitalizedIncomePrincipal) {
|
||||||
return repaymentPeriod -> {
|
return repaymentPeriod -> {
|
||||||
final boolean isChangeOnMaturityDate = isLastRepaymentPeriod(repaymentPeriod)
|
final boolean isChangeOnMaturityDate = isLastRepaymentPeriod(repaymentPeriod)
|
||||||
&& balanceChangeDate.isEqual(repaymentPeriod.getDueDate());
|
&& balanceChangeDate.isEqual(repaymentPeriod.getDueDate());
|
||||||
@@ -222,9 +223,10 @@ public class ProgressiveLoanInterestScheduleModel {
|
|||||||
isChangeOnMaturityDate);
|
isChangeOnMaturityDate);
|
||||||
if (interestPeriodOptional.isPresent()) {
|
if (interestPeriodOptional.isPresent()) {
|
||||||
interestPeriodOptional.get().addDisbursementAmount(disbursedAmount);
|
interestPeriodOptional.get().addDisbursementAmount(disbursedAmount);
|
||||||
|
interestPeriodOptional.get().addCapitalizedIncomePrincipalAmount(capitalizedIncomePrincipal);
|
||||||
interestPeriodOptional.get().addBalanceCorrectionAmount(correctionAmount);
|
interestPeriodOptional.get().addBalanceCorrectionAmount(correctionAmount);
|
||||||
} else {
|
} else {
|
||||||
insertInterestPeriod(repaymentPeriod, balanceChangeDate, disbursedAmount, correctionAmount);
|
insertInterestPeriod(repaymentPeriod, balanceChangeDate, disbursedAmount, correctionAmount, capitalizedIncomePrincipal);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -246,7 +248,7 @@ 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, Money capitalizedIncomePrincipal) {
|
||||||
final InterestPeriod previousInterestPeriod = findPreviousInterestPeriod(repaymentPeriod, balanceChangeDate);
|
final InterestPeriod previousInterestPeriod = findPreviousInterestPeriod(repaymentPeriod, balanceChangeDate);
|
||||||
final LocalDate originalDueDate = previousInterestPeriod.getDueDate();
|
final LocalDate originalDueDate = previousInterestPeriod.getDueDate();
|
||||||
final LocalDate newDueDate = calculateNewDueDate(previousInterestPeriod, balanceChangeDate);
|
final LocalDate newDueDate = calculateNewDueDate(previousInterestPeriod, balanceChangeDate);
|
||||||
@@ -254,6 +256,7 @@ public class ProgressiveLoanInterestScheduleModel {
|
|||||||
|
|
||||||
previousInterestPeriod.setDueDate(newDueDate);
|
previousInterestPeriod.setDueDate(newDueDate);
|
||||||
previousInterestPeriod.addDisbursementAmount(disbursedAmount);
|
previousInterestPeriod.addDisbursementAmount(disbursedAmount);
|
||||||
|
previousInterestPeriod.addCapitalizedIncomePrincipalAmount(capitalizedIncomePrincipal);
|
||||||
previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount);
|
previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount);
|
||||||
|
|
||||||
final InterestPeriod interestPeriod = InterestPeriod.withEmptyAmounts(repaymentPeriod, newDueDate, originalDueDate, isPaused);
|
final InterestPeriod interestPeriod = InterestPeriod.withEmptyAmounts(repaymentPeriod, newDueDate, originalDueDate, isPaused);
|
||||||
|
|||||||
+16
-1
@@ -225,6 +225,17 @@ public final class RepaymentPeriod {
|
|||||||
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)), mc); //
|
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)), mc); //
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sum of capitalized income principals
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Money getCapitalizedIncomePrincipal() {
|
||||||
|
return MathUtil.negativeToZero(interestPeriods.stream() //
|
||||||
|
.map(InterestPeriod::getCapitalizedIncomePrincipal) //
|
||||||
|
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)), mc); //
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives back due principal + credited principal or paid principal
|
* Gives back due principal + credited principal or paid principal
|
||||||
*
|
*
|
||||||
@@ -278,6 +289,7 @@ public final class RepaymentPeriod {
|
|||||||
InterestPeriod lastInterestPeriod = getInterestPeriods().get(getInterestPeriods().size() - 1);
|
InterestPeriod lastInterestPeriod = getInterestPeriods().get(getInterestPeriods().size() - 1);
|
||||||
Money calculatedOutStandingLoanBalance = lastInterestPeriod.getOutstandingLoanBalance() //
|
Money calculatedOutStandingLoanBalance = lastInterestPeriod.getOutstandingLoanBalance() //
|
||||||
.plus(lastInterestPeriod.getBalanceCorrectionAmount(), mc) //
|
.plus(lastInterestPeriod.getBalanceCorrectionAmount(), mc) //
|
||||||
|
.plus(lastInterestPeriod.getCapitalizedIncomePrincipal(), mc) //
|
||||||
.plus(lastInterestPeriod.getDisbursementAmount(), mc) //
|
.plus(lastInterestPeriod.getDisbursementAmount(), mc) //
|
||||||
.minus(getDuePrincipal(), mc)//
|
.minus(getDuePrincipal(), mc)//
|
||||||
.plus(getPaidPrincipal(), mc);//
|
.plus(getPaidPrincipal(), mc);//
|
||||||
@@ -305,7 +317,10 @@ public final class RepaymentPeriod {
|
|||||||
Money totalDisbursedAmount = getInterestPeriods().stream() //
|
Money totalDisbursedAmount = getInterestPeriods().stream() //
|
||||||
.map(InterestPeriod::getDisbursementAmount) //
|
.map(InterestPeriod::getDisbursementAmount) //
|
||||||
.reduce(getZero(mc), (m1, m2) -> m1.plus(m2, mc)); //
|
.reduce(getZero(mc), (m1, m2) -> m1.plus(m2, mc)); //
|
||||||
return initialBalance.add(totalDisbursedAmount, mc);
|
Money totalCapitalizedIncomeAmount = getInterestPeriods().stream() //
|
||||||
|
.map(InterestPeriod::getCapitalizedIncomePrincipal) //
|
||||||
|
.reduce(getZero(mc), (m1, m2) -> m1.plus(m2, mc)); //
|
||||||
|
return initialBalance.add(totalDisbursedAmount, mc).add(totalCapitalizedIncomeAmount, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Money getZero(MathContext mc) {
|
private Money getZero(MathContext mc) {
|
||||||
|
|||||||
+1
@@ -464,6 +464,7 @@ public class LoanImportHandler implements ImportHandler {
|
|||||||
JsonObject loanRepaymentJsonob = gsonBuilder.create().toJsonTree(loanRepayments.get(rowIndex)).getAsJsonObject();
|
JsonObject loanRepaymentJsonob = gsonBuilder.create().toJsonTree(loanRepayments.get(rowIndex)).getAsJsonObject();
|
||||||
loanRepaymentJsonob.remove("manuallyReversed");
|
loanRepaymentJsonob.remove("manuallyReversed");
|
||||||
loanRepaymentJsonob.remove("numberOfRepayments");
|
loanRepaymentJsonob.remove("numberOfRepayments");
|
||||||
|
loanRepaymentJsonob.remove("reversed");
|
||||||
String payload = loanRepaymentJsonob.toString();
|
String payload = loanRepaymentJsonob.toString();
|
||||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||||
.loanRepaymentTransaction(result.getLoanId()) //
|
.loanRepaymentTransaction(result.getLoanId()) //
|
||||||
|
|||||||
+9
-2
@@ -139,6 +139,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeTyp
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryBalancesRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryBalancesRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTemplateTypeRequiredException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanTemplateTypeRequiredException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.NotSupportedLoanTemplateTypeException;
|
import org.apache.fineract.portfolio.loanaccount.exception.NotSupportedLoanTemplateTypeException;
|
||||||
@@ -150,6 +151,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul
|
|||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryReadPlatformService;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryReadPlatformService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
||||||
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.GLIMAccountInfoReadPlatformService;
|
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
|
||||||
@@ -297,6 +299,7 @@ public class LoansApiResource {
|
|||||||
private final ClientReadPlatformService clientReadPlatformService;
|
private final ClientReadPlatformService clientReadPlatformService;
|
||||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||||
private final LoanSummaryProviderDelegate loanSummaryProviderDelegate;
|
private final LoanSummaryProviderDelegate loanSummaryProviderDelegate;
|
||||||
|
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This template API is used for loan approval, ideally this should be invoked on loan that are pending for
|
* This template API is used for loan approval, ideally this should be invoked on loan that are pending for
|
||||||
@@ -498,11 +501,13 @@ public class LoansApiResource {
|
|||||||
loanBasicDetails.getPageItems().forEach(i -> {
|
loanBasicDetails.getPageItems().forEach(i -> {
|
||||||
if (i.getSummary() != null) {
|
if (i.getSummary() != null) {
|
||||||
Collection<DisbursementData> disbursementData = this.loanReadPlatformService.retrieveLoanDisbursementDetails(i.getId());
|
Collection<DisbursementData> disbursementData = this.loanReadPlatformService.retrieveLoanDisbursementDetails(i.getId());
|
||||||
|
List<LoanTransactionRepaymentPeriodData> capitalizedIncomeData = this.loanCapitalizedIncomeBalanceRepository
|
||||||
|
.findRepaymentPeriodDataByLoanId(i.getId());
|
||||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = new RepaymentScheduleRelatedLoanData(
|
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = new RepaymentScheduleRelatedLoanData(
|
||||||
i.getTimeline().getExpectedDisbursementDate(), i.getTimeline().getActualDisbursementDate(), i.getCurrency(),
|
i.getTimeline().getExpectedDisbursementDate(), i.getTimeline().getActualDisbursementDate(), i.getCurrency(),
|
||||||
i.getPrincipal(), i.getInArrearsTolerance(), i.getFeeChargesAtDisbursementCharged());
|
i.getPrincipal(), i.getInArrearsTolerance(), i.getFeeChargesAtDisbursementCharged());
|
||||||
final LoanScheduleData repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(i.getId(),
|
final LoanScheduleData repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(i.getId(),
|
||||||
repaymentScheduleRelatedData, disbursementData, i.isInterestRecalculationEnabled(),
|
repaymentScheduleRelatedData, disbursementData, capitalizedIncomeData, i.isInterestRecalculationEnabled(),
|
||||||
LoanScheduleType.fromEnumOptionData(i.getLoanScheduleType()));
|
LoanScheduleType.fromEnumOptionData(i.getLoanScheduleType()));
|
||||||
LoanSummaryDataProvider loanSummaryDataProvider = loanSummaryProviderDelegate
|
LoanSummaryDataProvider loanSummaryDataProvider = loanSummaryProviderDelegate
|
||||||
.resolveLoanSummaryDataProvider(i.getTransactionProcessingStrategyCode());
|
.resolveLoanSummaryDataProvider(i.getTransactionProcessingStrategyCode());
|
||||||
@@ -981,13 +986,15 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
|
if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
|
||||||
mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName);
|
mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName);
|
||||||
|
List<LoanTransactionRepaymentPeriodData> capitalizedIncomeData = this.loanCapitalizedIncomeBalanceRepository
|
||||||
|
.findRepaymentPeriodDataByLoanId(resolvedLoanId);
|
||||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = new RepaymentScheduleRelatedLoanData(
|
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = new RepaymentScheduleRelatedLoanData(
|
||||||
loanBasicDetails.getTimeline().getExpectedDisbursementDate(),
|
loanBasicDetails.getTimeline().getExpectedDisbursementDate(),
|
||||||
loanBasicDetails.getTimeline().getActualDisbursementDate(), loanBasicDetails.getCurrency(),
|
loanBasicDetails.getTimeline().getActualDisbursementDate(), loanBasicDetails.getCurrency(),
|
||||||
loanBasicDetails.getPrincipal(), loanBasicDetails.getInArrearsTolerance(),
|
loanBasicDetails.getPrincipal(), loanBasicDetails.getInArrearsTolerance(),
|
||||||
loanBasicDetails.getFeeChargesAtDisbursementCharged());
|
loanBasicDetails.getFeeChargesAtDisbursementCharged());
|
||||||
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(resolvedLoanId, repaymentScheduleRelatedData,
|
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(resolvedLoanId, repaymentScheduleRelatedData,
|
||||||
disbursementData, loanBasicDetails.isInterestRecalculationEnabled(),
|
disbursementData, capitalizedIncomeData, loanBasicDetails.isInterestRecalculationEnabled(),
|
||||||
LoanScheduleType.fromEnumOptionData(loanBasicDetails.getLoanScheduleType()));
|
LoanScheduleType.fromEnumOptionData(loanBasicDetails.getLoanScheduleType()));
|
||||||
|
|
||||||
if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName)
|
if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName)
|
||||||
|
|||||||
+153
-149
@@ -42,9 +42,9 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "[2012, 4, 3]")
|
@Schema(example = "[2012, 4, 3]")
|
||||||
public LocalDate approvalDate;
|
public LocalDate approvalDate;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double approvalAmount;
|
public BigDecimal approvalAmount;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double netDisbursalAmount;
|
public BigDecimal netDisbursalAmount;
|
||||||
|
|
||||||
public GetLoanCurrency currency;
|
public GetLoanCurrency currency;
|
||||||
}
|
}
|
||||||
@@ -289,29 +289,29 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "30")
|
@Schema(example = "30")
|
||||||
public Long loanTermInDays;
|
public Long loanTermInDays;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double totalPrincipalDisbursed;
|
public BigDecimal totalPrincipalDisbursed;
|
||||||
@Schema(example = "200.00")
|
@Schema(example = "200.00")
|
||||||
public Double totalPrincipalExpected;
|
public BigDecimal totalPrincipalExpected;
|
||||||
@Schema(example = "200.00")
|
@Schema(example = "200.00")
|
||||||
public Double totalPrincipalPaid;
|
public BigDecimal totalPrincipalPaid;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double totalInterestCharged;
|
public BigDecimal totalInterestCharged;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double totalFeeChargesCharged;
|
public BigDecimal totalFeeChargesCharged;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double totalPenaltyChargesCharged;
|
public BigDecimal totalPenaltyChargesCharged;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double totalWaived;
|
public BigDecimal totalWaived;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double totalWrittenOff;
|
public BigDecimal totalWrittenOff;
|
||||||
@Schema(example = "200.00")
|
@Schema(example = "200.00")
|
||||||
public Double totalRepaymentExpected;
|
public BigDecimal totalRepaymentExpected;
|
||||||
@Schema(example = "200.00")
|
@Schema(example = "200.00")
|
||||||
public Double totalPaidInAdvance;
|
public BigDecimal totalPaidInAdvance;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double totalPaidLate;
|
public BigDecimal totalPaidLate;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double totalOutstanding;
|
public BigDecimal totalOutstanding;
|
||||||
public List<GetLoansLoanIdRepaymentPeriod> periods;
|
public List<GetLoansLoanIdRepaymentPeriod> periods;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,71 +332,71 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "30")
|
@Schema(example = "30")
|
||||||
public Long daysInPeriod;
|
public Long daysInPeriod;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double principalOriginalDue;
|
public BigDecimal principalOriginalDue;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double principalDue;
|
public BigDecimal principalDue;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double principalPaid;
|
public BigDecimal principalPaid;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double principalWrittenOff;
|
public BigDecimal principalWrittenOff;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double principalOutstanding;
|
public BigDecimal principalOutstanding;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double principalLoanBalanceOutstanding;
|
public BigDecimal principalLoanBalanceOutstanding;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestOriginalDue;
|
public BigDecimal interestOriginalDue;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestDue;
|
public BigDecimal interestDue;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestPaid;
|
public BigDecimal interestPaid;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestWaived;
|
public BigDecimal interestWaived;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestWrittenOff;
|
public BigDecimal interestWrittenOff;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestOutstanding;
|
public BigDecimal interestOutstanding;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double feeChargesDue;
|
public BigDecimal feeChargesDue;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double feeChargesPaid;
|
public BigDecimal feeChargesPaid;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double feeChargesWaived;
|
public BigDecimal feeChargesWaived;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double feeChargesWrittenOff;
|
public BigDecimal feeChargesWrittenOff;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double feeChargesOutstanding;
|
public BigDecimal feeChargesOutstanding;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double penaltyChargesDue;
|
public BigDecimal penaltyChargesDue;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double penaltyChargesPaid;
|
public BigDecimal penaltyChargesPaid;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double penaltyChargesWaived;
|
public BigDecimal penaltyChargesWaived;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double penaltyChargesWrittenOff;
|
public BigDecimal penaltyChargesWrittenOff;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double penaltyChargesOutstanding;
|
public BigDecimal penaltyChargesOutstanding;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double totalOriginalDueForPeriod;
|
public BigDecimal totalOriginalDueForPeriod;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double totalDueForPeriod;
|
public BigDecimal totalDueForPeriod;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double totalPaidForPeriod;
|
public BigDecimal totalPaidForPeriod;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double totalPaidInAdvanceForPeriod;
|
public BigDecimal totalPaidInAdvanceForPeriod;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double totalPaidLateForPeriod;
|
public BigDecimal totalPaidLateForPeriod;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double totalWaivedForPeriod;
|
public BigDecimal totalWaivedForPeriod;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double totalWrittenOffForPeriod;
|
public BigDecimal totalWrittenOffForPeriod;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double totalOutstandingForPeriod;
|
public BigDecimal totalOutstandingForPeriod;
|
||||||
@Schema(example = "20.000000")
|
@Schema(example = "20.000000")
|
||||||
public Double totalActualCostOfLoanForPeriod;
|
public BigDecimal totalActualCostOfLoanForPeriod;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double totalInstallmentAmountForPeriod;
|
public BigDecimal totalInstallmentAmountForPeriod;
|
||||||
@Schema(example = "2.000000")
|
@Schema(example = "2.000000")
|
||||||
public Double totalCredits;
|
public BigDecimal totalCredits;
|
||||||
@Schema(example = "true")
|
@Schema(example = "true")
|
||||||
public Boolean downPaymentPeriod;
|
public Boolean downPaymentPeriod;
|
||||||
}
|
}
|
||||||
@@ -412,15 +412,15 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "[2022, 07, 01]")
|
@Schema(example = "[2022, 07, 01]")
|
||||||
public LocalDate actualDisbursementDate;
|
public LocalDate actualDisbursementDate;
|
||||||
@Schema(example = "22000.000000")
|
@Schema(example = "22000.000000")
|
||||||
public Double principal;
|
public BigDecimal principal;
|
||||||
@Schema(example = "22000.000000")
|
@Schema(example = "22000.000000")
|
||||||
public Double netDisbursalAmount;
|
public BigDecimal netDisbursalAmount;
|
||||||
@Schema(example = "1")
|
@Schema(example = "1")
|
||||||
public String loanChargeId;
|
public String loanChargeId;
|
||||||
@Schema(example = "22000.000000")
|
@Schema(example = "22000.000000")
|
||||||
public Double chargeAmount;
|
public BigDecimal chargeAmount;
|
||||||
@Schema(example = "22000.000000")
|
@Schema(example = "22000.000000")
|
||||||
public Double waivedChargeAmount;
|
public BigDecimal waivedChargeAmount;
|
||||||
@Schema(example = "dd MMMM yyyy")
|
@Schema(example = "dd MMMM yyyy")
|
||||||
public String dateFormat;
|
public String dateFormat;
|
||||||
@Schema(example = "de_DE")
|
@Schema(example = "de_DE")
|
||||||
@@ -517,77 +517,81 @@ final class LoansApiResourceSwagger {
|
|||||||
|
|
||||||
public GetLoansLoanIdCurrency currency;
|
public GetLoansLoanIdCurrency currency;
|
||||||
@Schema(example = "1000000.000000")
|
@Schema(example = "1000000.000000")
|
||||||
public Double principalDisbursed;
|
public BigDecimal totalPrincipal;
|
||||||
@Schema(example = "0.000000")
|
|
||||||
public Double principalPaid;
|
|
||||||
@Schema(example = "0.00")
|
|
||||||
public Double principalAdjustments;
|
|
||||||
@Schema(example = "0.000000")
|
|
||||||
public Double principalWrittenOff;
|
|
||||||
@Schema(example = "1000000.000000")
|
@Schema(example = "1000000.000000")
|
||||||
public Double principalOutstanding;
|
public BigDecimal principalDisbursed;
|
||||||
|
@Schema(example = "1000000.000000")
|
||||||
|
public BigDecimal totalPrincipalFromCapitalizedIncome;
|
||||||
|
@Schema(example = "0.000000")
|
||||||
|
public BigDecimal principalPaid;
|
||||||
|
@Schema(example = "0.00")
|
||||||
|
public BigDecimal principalAdjustments;
|
||||||
|
@Schema(example = "0.000000")
|
||||||
|
public BigDecimal principalWrittenOff;
|
||||||
|
@Schema(example = "1000000.000000")
|
||||||
|
public BigDecimal principalOutstanding;
|
||||||
@Schema(example = "833333.300000")
|
@Schema(example = "833333.300000")
|
||||||
public Double principalOverdue;
|
public BigDecimal principalOverdue;
|
||||||
@Schema(example = "240000.000000")
|
@Schema(example = "240000.000000")
|
||||||
public Double interestCharged;
|
public BigDecimal interestCharged;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestPaid;
|
public BigDecimal interestPaid;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestWaived;
|
public BigDecimal interestWaived;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double interestWrittenOff;
|
public BigDecimal interestWrittenOff;
|
||||||
@Schema(example = "240000.000000")
|
@Schema(example = "240000.000000")
|
||||||
public Double interestOutstanding;
|
public BigDecimal interestOutstanding;
|
||||||
@Schema(example = "200000.000000")
|
@Schema(example = "200000.000000")
|
||||||
public Double interestOverdue;
|
public BigDecimal interestOverdue;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double feeAdjustments;
|
public BigDecimal feeAdjustments;
|
||||||
@Schema(example = "18000.000000")
|
@Schema(example = "18000.000000")
|
||||||
public Double feeChargesCharged;
|
public BigDecimal feeChargesCharged;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double feeChargesDueAtDisbursementCharged;
|
public BigDecimal feeChargesDueAtDisbursementCharged;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double feeChargesPaid;
|
public BigDecimal feeChargesPaid;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double feeChargesWaived;
|
public BigDecimal feeChargesWaived;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double feeChargesWrittenOff;
|
public BigDecimal feeChargesWrittenOff;
|
||||||
@Schema(example = "18000.000000")
|
@Schema(example = "18000.000000")
|
||||||
public Double feeChargesOutstanding;
|
public BigDecimal feeChargesOutstanding;
|
||||||
@Schema(example = "15000.000000")
|
@Schema(example = "15000.000000")
|
||||||
public Double feeChargesOverdue;
|
public BigDecimal feeChargesOverdue;
|
||||||
@Schema(example = "0.00")
|
@Schema(example = "0.00")
|
||||||
public Double penaltyAdjustments;
|
public BigDecimal penaltyAdjustments;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double penaltyChargesCharged;
|
public BigDecimal penaltyChargesCharged;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double penaltyChargesPaid;
|
public BigDecimal penaltyChargesPaid;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double penaltyChargesWaived;
|
public BigDecimal penaltyChargesWaived;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double penaltyChargesWrittenOff;
|
public BigDecimal penaltyChargesWrittenOff;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double penaltyChargesOutstanding;
|
public BigDecimal penaltyChargesOutstanding;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double penaltyChargesOverdue;
|
public BigDecimal penaltyChargesOverdue;
|
||||||
@Schema(example = "1258000.000000")
|
@Schema(example = "1258000.000000")
|
||||||
public Double totalExpectedRepayment;
|
public BigDecimal totalExpectedRepayment;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalRepayment;
|
public BigDecimal totalRepayment;
|
||||||
@Schema(example = "258000.000000")
|
@Schema(example = "258000.000000")
|
||||||
public Double totalExpectedCostOfLoan;
|
public BigDecimal totalExpectedCostOfLoan;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalCostOfLoan;
|
public BigDecimal totalCostOfLoan;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalWaived;
|
public BigDecimal totalWaived;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalWrittenOff;
|
public BigDecimal totalWrittenOff;
|
||||||
@Schema(example = "1258000.000000")
|
@Schema(example = "1258000.000000")
|
||||||
public Double totalOutstanding;
|
public BigDecimal totalOutstanding;
|
||||||
@Schema(example = "1048333.30000")
|
@Schema(example = "1048333.30000")
|
||||||
public Double totalOverdue;
|
public BigDecimal totalOverdue;
|
||||||
@Schema(example = "2456.30000")
|
@Schema(example = "2456.30000")
|
||||||
public Double totalRecovered;
|
public BigDecimal totalRecovered;
|
||||||
@Schema(example = "[2012, 5, 10]")
|
@Schema(example = "[2012, 5, 10]")
|
||||||
public LocalDate overdueSinceDate;
|
public LocalDate overdueSinceDate;
|
||||||
@Schema(example = "1")
|
@Schema(example = "1")
|
||||||
@@ -597,9 +601,9 @@ final class LoansApiResourceSwagger {
|
|||||||
public GetLoansLoanIdLinkedAccount linkedAccount;
|
public GetLoansLoanIdLinkedAccount linkedAccount;
|
||||||
public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
|
public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
|
||||||
@Schema(example = "1100.000000")
|
@Schema(example = "1100.000000")
|
||||||
public Double fixedEmiAmount;
|
public BigDecimal fixedEmiAmount;
|
||||||
@Schema(example = "35000.000000")
|
@Schema(example = "35000.000000")
|
||||||
public Double maxOutstandingLoanBalance;
|
public BigDecimal maxOutstandingLoanBalance;
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public Boolean canDisburse;
|
public Boolean canDisburse;
|
||||||
@Schema(example = "true")
|
@Schema(example = "true")
|
||||||
@@ -607,33 +611,33 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public Boolean isNPA;
|
public Boolean isNPA;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalMerchantRefund;
|
public BigDecimal totalMerchantRefund;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalMerchantRefundReversed;
|
public BigDecimal totalMerchantRefundReversed;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalPayoutRefund;
|
public BigDecimal totalPayoutRefund;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalPayoutRefundReversed;
|
public BigDecimal totalPayoutRefundReversed;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalGoodwillCredit;
|
public BigDecimal totalGoodwillCredit;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalGoodwillCreditReversed;
|
public BigDecimal totalGoodwillCreditReversed;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalChargeAdjustment;
|
public BigDecimal totalChargeAdjustment;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalChargeAdjustmentReversed;
|
public BigDecimal totalChargeAdjustmentReversed;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalChargeback;
|
public BigDecimal totalChargeback;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalCreditBalanceRefund;
|
public BigDecimal totalCreditBalanceRefund;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalCreditBalanceRefundReversed;
|
public BigDecimal totalCreditBalanceRefundReversed;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalRepaymentTransaction;
|
public BigDecimal totalRepaymentTransaction;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalInterestPaymentWaiver;
|
public BigDecimal totalInterestPaymentWaiver;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalRepaymentTransactionReversed;
|
public BigDecimal totalRepaymentTransactionReversed;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public BigDecimal totalUnpaidPayableDueInterest;
|
public BigDecimal totalUnpaidPayableDueInterest;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
@@ -754,7 +758,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "11")
|
@Schema(example = "11")
|
||||||
public Long id;
|
public Long id;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double amount;
|
public BigDecimal amount;
|
||||||
@Schema(example = "9679")
|
@Schema(example = "9679")
|
||||||
public Integer installmentNumber;
|
public Integer installmentNumber;
|
||||||
@Schema(example = "1")
|
@Schema(example = "1")
|
||||||
@@ -810,7 +814,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "CHARGEBACK")
|
@Schema(example = "CHARGEBACK")
|
||||||
public String relationType;
|
public String relationType;
|
||||||
@Schema(example = "100.00")
|
@Schema(example = "100.00")
|
||||||
public Double amount;
|
public BigDecimal amount;
|
||||||
@Schema(example = "Repayment Adjustment Chargeback")
|
@Schema(example = "Repayment Adjustment Chargeback")
|
||||||
public String paymentType;
|
public String paymentType;
|
||||||
|
|
||||||
@@ -831,27 +835,27 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(description = "Payment detail")
|
@Schema(description = "Payment detail")
|
||||||
public GetLoansLoanIdPaymentDetailData paymentDetailData;
|
public GetLoansLoanIdPaymentDetailData paymentDetailData;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double amount;
|
public BigDecimal amount;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double netDisbursalAmount;
|
public BigDecimal netDisbursalAmount;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double principalPortion;
|
public BigDecimal principalPortion;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double interestPortion;
|
public BigDecimal interestPortion;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double feeChargesPortion;
|
public BigDecimal feeChargesPortion;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double penaltyChargesPortion;
|
public BigDecimal penaltyChargesPortion;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double overpaymentPortion;
|
public BigDecimal overpaymentPortion;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double unrecognizedIncomePortion;
|
public BigDecimal unrecognizedIncomePortion;
|
||||||
@Schema(example = "3")
|
@Schema(example = "3")
|
||||||
public String externalId;
|
public String externalId;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double fixedEmiAmount;
|
public BigDecimal fixedEmiAmount;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double outstandingLoanBalance;
|
public BigDecimal outstandingLoanBalance;
|
||||||
@Schema(example = "[2022, 07, 01]")
|
@Schema(example = "[2022, 07, 01]")
|
||||||
public LocalDate submittedOnDate;
|
public LocalDate submittedOnDate;
|
||||||
public boolean manuallyReversed;
|
public boolean manuallyReversed;
|
||||||
@@ -870,7 +874,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "de_DE")
|
@Schema(example = "de_DE")
|
||||||
public String locale;
|
public String locale;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double transactionAmount;
|
public BigDecimal transactionAmount;
|
||||||
@Schema(example = "[2022, 07, 01]")
|
@Schema(example = "[2022, 07, 01]")
|
||||||
public LocalDate transactionDate;
|
public LocalDate transactionDate;
|
||||||
@Schema(example = "101")
|
@Schema(example = "101")
|
||||||
@@ -924,19 +928,19 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "[2022, 07, 01]")
|
@Schema(example = "[2022, 07, 01]")
|
||||||
public LocalDate dueDate;
|
public LocalDate dueDate;
|
||||||
@Schema(example = "13.560000")
|
@Schema(example = "13.560000")
|
||||||
public Double amount;
|
public BigDecimal amount;
|
||||||
@Schema(example = "13.560000")
|
@Schema(example = "13.560000")
|
||||||
public Double amountOutstanding;
|
public BigDecimal amountOutstanding;
|
||||||
@Schema(example = "13.560000")
|
@Schema(example = "13.560000")
|
||||||
public Double amountWaived;
|
public BigDecimal amountWaived;
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public boolean paid;
|
public boolean paid;
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public boolean waived;
|
public boolean waived;
|
||||||
@Schema(example = "13.560000")
|
@Schema(example = "13.560000")
|
||||||
public Double amountAccrued;
|
public BigDecimal amountAccrued;
|
||||||
@Schema(example = "13.560000")
|
@Schema(example = "13.560000")
|
||||||
public Double amountUnrecognized;
|
public BigDecimal amountUnrecognized;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(example = "3")
|
@Schema(example = "3")
|
||||||
@@ -952,23 +956,23 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(description = "Enum option data")
|
@Schema(description = "Enum option data")
|
||||||
public GetLoansLoanIdEnumOptionData chargeCalculationType;
|
public GetLoansLoanIdEnumOptionData chargeCalculationType;
|
||||||
@Schema(example = "3.400000")
|
@Schema(example = "3.400000")
|
||||||
public Double percentage;
|
public BigDecimal percentage;
|
||||||
@Schema(example = "13.560000")
|
@Schema(example = "13.560000")
|
||||||
public Double amountPercentageAppliedTo;
|
public BigDecimal amountPercentageAppliedTo;
|
||||||
@Schema(description = "currency")
|
@Schema(description = "currency")
|
||||||
public GetLoansLoanIdCurrency currency;
|
public GetLoansLoanIdCurrency currency;
|
||||||
@Schema(example = "102.000000")
|
@Schema(example = "102.000000")
|
||||||
public Double amount;
|
public BigDecimal amount;
|
||||||
@Schema(example = "12.000000")
|
@Schema(example = "12.000000")
|
||||||
public Double amountPaid;
|
public BigDecimal amountPaid;
|
||||||
@Schema(example = "14.000000")
|
@Schema(example = "14.000000")
|
||||||
public Double amountWaived;
|
public BigDecimal amountWaived;
|
||||||
@Schema(example = "102.000000")
|
@Schema(example = "102.000000")
|
||||||
public Double amountWrittenOff;
|
public BigDecimal amountWrittenOff;
|
||||||
@Schema(example = "102.000000")
|
@Schema(example = "102.000000")
|
||||||
public Double amountOutstanding;
|
public BigDecimal amountOutstanding;
|
||||||
@Schema(example = "102.000000")
|
@Schema(example = "102.000000")
|
||||||
public Double amountOrPercentage;
|
public BigDecimal amountOrPercentage;
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public boolean penalty;
|
public boolean penalty;
|
||||||
@Schema(description = "Enum option data")
|
@Schema(description = "Enum option data")
|
||||||
@@ -982,15 +986,15 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "3")
|
@Schema(example = "3")
|
||||||
public Long loanId;
|
public Long loanId;
|
||||||
@Schema(example = "30.000000")
|
@Schema(example = "30.000000")
|
||||||
public Double minCap;
|
public BigDecimal minCap;
|
||||||
@Schema(example = "30.000000")
|
@Schema(example = "30.000000")
|
||||||
public Double maxCap;
|
public BigDecimal maxCap;
|
||||||
@Schema(description = "List of GetLoansLoanIdLoanInstallmentChargeData")
|
@Schema(description = "List of GetLoansLoanIdLoanInstallmentChargeData")
|
||||||
public List<GetLoansLoanIdLoanInstallmentChargeData> installmentChargeData;
|
public List<GetLoansLoanIdLoanInstallmentChargeData> installmentChargeData;
|
||||||
@Schema(example = "30.000000")
|
@Schema(example = "30.000000")
|
||||||
private Double amountAccrued;
|
private BigDecimal amountAccrued;
|
||||||
@Schema(example = "30.000000")
|
@Schema(example = "30.000000")
|
||||||
private Double amountUnrecognized;
|
private BigDecimal amountUnrecognized;
|
||||||
@Schema(example = "3ert3453")
|
@Schema(example = "3ert3453")
|
||||||
private String externalId;
|
private String externalId;
|
||||||
}
|
}
|
||||||
@@ -1000,7 +1004,7 @@ final class LoansApiResourceSwagger {
|
|||||||
private GetLoansLoanIdDelinquencySummary() {}
|
private GetLoansLoanIdDelinquencySummary() {}
|
||||||
|
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double availableDisbursementAmount;
|
public BigDecimal availableDisbursementAmount;
|
||||||
@Schema(example = "12")
|
@Schema(example = "12")
|
||||||
public Integer pastDueDays;
|
public Integer pastDueDays;
|
||||||
@Schema(example = "[2022, 07, 01]")
|
@Schema(example = "[2022, 07, 01]")
|
||||||
@@ -1010,7 +1014,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "[2022, 07, 01]")
|
@Schema(example = "[2022, 07, 01]")
|
||||||
public LocalDate delinquentDate;
|
public LocalDate delinquentDate;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double delinquentAmount;
|
public BigDecimal delinquentAmount;
|
||||||
@Schema(example = "80.000000")
|
@Schema(example = "80.000000")
|
||||||
public BigDecimal delinquentPrincipal;
|
public BigDecimal delinquentPrincipal;
|
||||||
@Schema(example = "10.000000")
|
@Schema(example = "10.000000")
|
||||||
@@ -1022,12 +1026,12 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "[2022, 07, 01]")
|
@Schema(example = "[2022, 07, 01]")
|
||||||
public LocalDate lastPaymentDate;
|
public LocalDate lastPaymentDate;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double lastPaymentAmount;
|
public BigDecimal lastPaymentAmount;
|
||||||
|
|
||||||
@Schema(example = "[2022, 07, 01]")
|
@Schema(example = "[2022, 07, 01]")
|
||||||
public LocalDate lastRepaymentDate;
|
public LocalDate lastRepaymentDate;
|
||||||
@Schema(example = "100.000000")
|
@Schema(example = "100.000000")
|
||||||
public Double lastRepaymentAmount;
|
public BigDecimal lastRepaymentAmount;
|
||||||
|
|
||||||
@Schema(description = "List of GetLoansLoanIdDelinquencyPausePeriod")
|
@Schema(description = "List of GetLoansLoanIdDelinquencyPausePeriod")
|
||||||
public List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriods;
|
public List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriods;
|
||||||
@@ -1094,7 +1098,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "[2024, 1, 1]")
|
@Schema(example = "[2024, 1, 1]")
|
||||||
public LocalDate termVariationApplicableFrom;
|
public LocalDate termVariationApplicableFrom;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double decimalValue;
|
public BigDecimal decimalValue;
|
||||||
@Schema(example = "[2024, 1, 1]")
|
@Schema(example = "[2024, 1, 1]")
|
||||||
public LocalDate dateValue;
|
public LocalDate dateValue;
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
@@ -1139,11 +1143,11 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "1000000.00")
|
@Schema(example = "1000000.00")
|
||||||
public BigDecimal principal;
|
public BigDecimal principal;
|
||||||
@Schema(example = "1000.000000")
|
@Schema(example = "1000.000000")
|
||||||
public Double approvedPrincipal;
|
public BigDecimal approvedPrincipal;
|
||||||
@Schema(example = "1001.000000")
|
@Schema(example = "1001.000000")
|
||||||
public Double proposedPrincipal;
|
public BigDecimal proposedPrincipal;
|
||||||
@Schema(example = "200.000000")
|
@Schema(example = "200.000000")
|
||||||
public Double netDisbursalAmount;
|
public BigDecimal netDisbursalAmount;
|
||||||
@Schema(example = "12")
|
@Schema(example = "12")
|
||||||
public Integer termFrequency;
|
public Integer termFrequency;
|
||||||
public GetLoansLoanIdTermPeriodFrequencyType termPeriodFrequencyType;
|
public GetLoansLoanIdTermPeriodFrequencyType termPeriodFrequencyType;
|
||||||
@@ -1185,7 +1189,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public Boolean enableInstallmentLevelDelinquency;
|
public Boolean enableInstallmentLevelDelinquency;
|
||||||
@Schema(example = "250.000000")
|
@Schema(example = "250.000000")
|
||||||
public Double totalOverpaid;
|
public BigDecimal totalOverpaid;
|
||||||
public LocalDate lastClosedBusinessDate;
|
public LocalDate lastClosedBusinessDate;
|
||||||
@Schema(example = "[2013, 11, 1]")
|
@Schema(example = "[2013, 11, 1]")
|
||||||
public LocalDate overpaidOnDate;
|
public LocalDate overpaidOnDate;
|
||||||
@@ -1240,7 +1244,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "1 November 2023")
|
@Schema(example = "1 November 2023")
|
||||||
public String expectedDisbursementDate;
|
public String expectedDisbursementDate;
|
||||||
@Schema(example = "1000.00")
|
@Schema(example = "1000.00")
|
||||||
public Double principal;
|
public BigDecimal principal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PostLoansRequest() {}
|
private PostLoansRequest() {}
|
||||||
@@ -1387,7 +1391,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
public Long totalPrincipalPaid;
|
public Long totalPrincipalPaid;
|
||||||
@Schema(example = "13471.52")
|
@Schema(example = "13471.52")
|
||||||
public Double totalInterestCharged;
|
public BigDecimal totalInterestCharged;
|
||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
public Long totalFeeChargesCharged;
|
public Long totalFeeChargesCharged;
|
||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
@@ -1397,7 +1401,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
public Long totalWrittenOff;
|
public Long totalWrittenOff;
|
||||||
@Schema(example = "113471.52")
|
@Schema(example = "113471.52")
|
||||||
public Double totalRepaymentExpected;
|
public BigDecimal totalRepaymentExpected;
|
||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
public Long totalRepayment;
|
public Long totalRepayment;
|
||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
@@ -1616,7 +1620,7 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "[2012, 4, 3]")
|
@Schema(example = "[2012, 4, 3]")
|
||||||
public LocalDate expectedDisbursementDate;
|
public LocalDate expectedDisbursementDate;
|
||||||
@Schema(example = "22000")
|
@Schema(example = "22000")
|
||||||
public Double principal;
|
public BigDecimal principal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(example = "2")
|
@Schema(example = "2")
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 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.portfolio.loanaccount.data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LoanSchedulePeriodDataWrapper {
|
||||||
|
|
||||||
|
private final LoanPrincipalRelatedDataHolder data;
|
||||||
|
private final LocalDate date;
|
||||||
|
private final boolean isDisbursement;
|
||||||
|
}
|
||||||
+2
-1
@@ -97,7 +97,8 @@ public abstract class CommonLoanSummaryDataProvider implements LoanSummaryDataPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
return LoanSummaryData.builder().currency(defaultSummaryData.getCurrency())
|
return LoanSummaryData.builder().currency(defaultSummaryData.getCurrency())
|
||||||
.principalDisbursed(defaultSummaryData.getPrincipalDisbursed())
|
.principalDisbursed(defaultSummaryData.getPrincipalDisbursed()).totalPrincipal(defaultSummaryData.getTotalPrincipal())
|
||||||
|
.totalPrincipalFromCapitalizedIncome(defaultSummaryData.getTotalPrincipalFromCapitalizedIncome())
|
||||||
.principalAdjustments(defaultSummaryData.getPrincipalAdjustments()).principalPaid(defaultSummaryData.getPrincipalPaid())
|
.principalAdjustments(defaultSummaryData.getPrincipalAdjustments()).principalPaid(defaultSummaryData.getPrincipalPaid())
|
||||||
.principalWrittenOff(defaultSummaryData.getPrincipalWrittenOff())
|
.principalWrittenOff(defaultSummaryData.getPrincipalWrittenOff())
|
||||||
.principalOutstanding(defaultSummaryData.getPrincipalOutstanding())
|
.principalOutstanding(defaultSummaryData.getPrincipalOutstanding())
|
||||||
|
|||||||
+19
-9
@@ -21,10 +21,12 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
@@ -51,9 +53,10 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void processCapitalizedIncomeAmortizationOnLoanClosure(@NotNull final Loan loan) {
|
public void processCapitalizedIncomeAmortizationOnLoanClosure(@NotNull final Loan loan) {
|
||||||
final LocalDate transactionDate = getFinalCapitalizedIncomeAmortizationTransactionDate(loan);
|
final LocalDate transactionDate = getFinalCapitalizedIncomeAmortizationTransactionDate(loan);
|
||||||
final LoanTransaction amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate, false);
|
final Optional<LoanTransaction> amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate,
|
||||||
businessEventNotifierService
|
false);
|
||||||
.notifyPostBusinessEvent(new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(amortizationTransaction));
|
amortizationTransaction.ifPresent(loanTransaction -> businessEventNotifierService
|
||||||
|
.notifyPostBusinessEvent(new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(loanTransaction)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,13 +70,16 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
|||||||
transactionDate = DateUtils.getBusinessLocalDate();
|
transactionDate = DateUtils.getBusinessLocalDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
final LoanTransaction amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate, true);
|
final Optional<LoanTransaction> amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate,
|
||||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
true);
|
||||||
businessEventNotifierService
|
if (amortizationTransaction.isPresent()) {
|
||||||
.notifyPostBusinessEvent(new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(amortizationTransaction));
|
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
businessEventNotifierService.notifyPostBusinessEvent(
|
||||||
|
new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(amortizationTransaction.get()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanTransaction createCapitalizedIncomeAmortizationTransaction(final Loan loan, final LocalDate transactionDate,
|
private Optional<LoanTransaction> createCapitalizedIncomeAmortizationTransaction(final Loan loan, final LocalDate transactionDate,
|
||||||
final boolean isChargeOff) {
|
final boolean isChargeOff) {
|
||||||
ExternalId externalId = ExternalId.empty();
|
ExternalId externalId = ExternalId.empty();
|
||||||
BigDecimal totalUnrecognizedAmount = BigDecimal.ZERO;
|
BigDecimal totalUnrecognizedAmount = BigDecimal.ZERO;
|
||||||
@@ -92,13 +98,17 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
|||||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MathUtil.isLessThanOrEqualZero(totalUnrecognizedAmount)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
final LoanTransaction amortizationTransaction = LoanTransaction.capitalizedIncomeAmortization(loan, loan.getOffice(),
|
final LoanTransaction amortizationTransaction = LoanTransaction.capitalizedIncomeAmortization(loan, loan.getOffice(),
|
||||||
transactionDate, totalUnrecognizedAmount, externalId);
|
transactionDate, totalUnrecognizedAmount, externalId);
|
||||||
|
|
||||||
loan.addLoanTransaction(amortizationTransaction);
|
loan.addLoanTransaction(amortizationTransaction);
|
||||||
loanTransactionRepository.saveAndFlush(amortizationTransaction);
|
loanTransactionRepository.saveAndFlush(amortizationTransaction);
|
||||||
|
|
||||||
return amortizationTransaction;
|
return Optional.of(amortizationTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+105
-21
@@ -98,6 +98,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.LoanSchedulePeriodDataWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
||||||
@@ -118,6 +119,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||||
@@ -249,8 +251,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
accountData.getFeeChargesAtDisbursementCharged());
|
accountData.getFeeChargesAtDisbursementCharged());
|
||||||
|
|
||||||
final Collection<DisbursementData> disbursementData = retrieveLoanDisbursementDetails(accountData.getId());
|
final Collection<DisbursementData> disbursementData = retrieveLoanDisbursementDetails(accountData.getId());
|
||||||
|
List<LoanTransactionRepaymentPeriodData> capitalizedIncomeData = loanCapitalizedIncomeBalanceRepository
|
||||||
|
.findRepaymentPeriodDataByLoanId(accountData.getId());
|
||||||
final LoanScheduleData repaymentSchedule = retrieveRepaymentSchedule(accountData.getId(), repaymentScheduleRelatedData,
|
final LoanScheduleData repaymentSchedule = retrieveRepaymentSchedule(accountData.getId(), repaymentScheduleRelatedData,
|
||||||
disbursementData, accountData.isInterestRecalculationEnabled(),
|
disbursementData, capitalizedIncomeData, accountData.isInterestRecalculationEnabled(),
|
||||||
LoanScheduleType.fromEnumOptionData(accountData.getLoanScheduleType()));
|
LoanScheduleType.fromEnumOptionData(accountData.getLoanScheduleType()));
|
||||||
accountData.setRepaymentSchedule(repaymentSchedule);
|
accountData.setRepaymentSchedule(repaymentSchedule);
|
||||||
return accountData;
|
return accountData;
|
||||||
@@ -259,13 +263,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
@Override
|
@Override
|
||||||
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
|
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
|
||||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData,
|
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData,
|
||||||
boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType) {
|
Collection<LoanTransactionRepaymentPeriodData> capitalizedIncomeData, boolean isInterestRecalculationEnabled,
|
||||||
|
LoanScheduleType loanScheduleType) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.context.authenticatedUser();
|
this.context.authenticatedUser();
|
||||||
|
|
||||||
final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
|
final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
|
||||||
repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled, loanScheduleType);
|
repaymentScheduleRelatedLoanData, disbursementData, capitalizedIncomeData, isInterestRecalculationEnabled,
|
||||||
|
loanScheduleType);
|
||||||
final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
|
final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
|
||||||
|
|
||||||
return this.jdbcTemplate.query(sql, fullResultsetExtractor, loanId); // NOSONAR
|
return this.jdbcTemplate.query(sql, fullResultsetExtractor, loanId); // NOSONAR
|
||||||
@@ -749,7 +755,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
+ " l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, dbu.username as disbursedByUsername, dbu.firstname as disbursedByFirstname, dbu.lastname as disbursedByLastname,"
|
+ " l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, dbu.username as disbursedByUsername, dbu.firstname as disbursedByFirstname, dbu.lastname as disbursedByLastname,"
|
||||||
+ " l.closedon_date as closedOnDate, cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname, l.writtenoffon_date as writtenOffOnDate, "
|
+ " l.closedon_date as closedOnDate, cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname, l.writtenoffon_date as writtenOffOnDate, "
|
||||||
+ " l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate, l.interest_calculated_from_date as interestChargedFromDate, l.maturedon_date as actualMaturityDate, l.expected_maturedon_date as expectedMaturityDate, "
|
+ " l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate, l.interest_calculated_from_date as interestChargedFromDate, l.maturedon_date as actualMaturityDate, l.expected_maturedon_date as expectedMaturityDate, "
|
||||||
+ " l.principal_amount_proposed as proposedPrincipal, l.principal_amount as principal, l.approved_principal as approvedPrincipal, l.net_disbursal_amount as netDisbursalAmount, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery,"
|
+ " l.principal_amount_proposed as proposedPrincipal, l.principal_amount as principal, l.total_principal_derived as totalPrincipal, l.approved_principal as approvedPrincipal, l.net_disbursal_amount as netDisbursalAmount, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery,"
|
||||||
+ " l.grace_on_principal_periods as graceOnPrincipalPayment, l.recurring_moratorium_principal_periods as recurringMoratoriumOnPrincipalPeriods, l.grace_on_interest_periods as graceOnInterestPayment, l.grace_interest_free_periods as graceOnInterestCharged,l.grace_on_arrears_ageing as graceOnArrearsAgeing,"
|
+ " l.grace_on_principal_periods as graceOnPrincipalPayment, l.recurring_moratorium_principal_periods as recurringMoratoriumOnPrincipalPeriods, l.grace_on_interest_periods as graceOnInterestPayment, l.grace_interest_free_periods as graceOnInterestCharged,l.grace_on_arrears_ageing as graceOnArrearsAgeing,"
|
||||||
+ " l.nominal_interest_rate_per_period as interestRatePerPeriod, l.annual_nominal_interest_rate as annualInterestRate, "
|
+ " l.nominal_interest_rate_per_period as interestRatePerPeriod, l.annual_nominal_interest_rate as annualInterestRate, "
|
||||||
+ " l.repayment_period_frequency_enum as repaymentFrequencyType, l.interest_period_frequency_enum as interestRateFrequencyType, "
|
+ " l.repayment_period_frequency_enum as repaymentFrequencyType, l.interest_period_frequency_enum as interestRateFrequencyType, "
|
||||||
@@ -764,7 +770,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
+ sqlGenerator.escape("name")
|
+ sqlGenerator.escape("name")
|
||||||
+ " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "
|
+ " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "
|
||||||
+ " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName, "
|
+ " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName, "
|
||||||
+ " l.principal_disbursed_derived as principalDisbursed, l.principal_repaid_derived as principalPaid,"
|
+ " l.principal_disbursed_derived as principalDisbursed, l.capitalized_income_derived as totalPrincipalFromCapitalizedIncome, l.principal_repaid_derived as principalPaid,"
|
||||||
+ " l.principal_adjustments_derived as principalAdjustments, l.principal_writtenoff_derived as principalWrittenOff,"
|
+ " l.principal_adjustments_derived as principalAdjustments, l.principal_writtenoff_derived as principalWrittenOff,"
|
||||||
+ " l.fee_adjustments_derived as feeAdjustments, l.penalty_adjustments_derived as penaltyAdjustments,"
|
+ " l.fee_adjustments_derived as feeAdjustments, l.penalty_adjustments_derived as penaltyAdjustments,"
|
||||||
+ " l.principal_outstanding_derived as principalOutstanding, l.interest_charged_derived as interestCharged,"
|
+ " l.principal_outstanding_derived as principalOutstanding, l.interest_charged_derived as interestCharged,"
|
||||||
@@ -1023,6 +1029,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
|
|
||||||
// loan summary
|
// loan summary
|
||||||
final BigDecimal principalDisbursed = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDisbursed");
|
final BigDecimal principalDisbursed = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDisbursed");
|
||||||
|
final BigDecimal totalPrincipal = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalPrincipal");
|
||||||
|
final BigDecimal totalPrincipalFromCapitalizedIncome = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs,
|
||||||
|
"totalPrincipalFromCapitalizedIncome");
|
||||||
final BigDecimal principalAdjustments = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalAdjustments");
|
final BigDecimal principalAdjustments = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalAdjustments");
|
||||||
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
|
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
|
||||||
final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff");
|
final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff");
|
||||||
@@ -1066,6 +1075,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
inArrears = (overdueSinceDate != null);
|
inArrears = (overdueSinceDate != null);
|
||||||
|
|
||||||
loanSummary = LoanSummaryData.builder().currency(currencyData).principalDisbursed(principalDisbursed)
|
loanSummary = LoanSummaryData.builder().currency(currencyData).principalDisbursed(principalDisbursed)
|
||||||
|
.totalPrincipal(totalPrincipal).totalPrincipalFromCapitalizedIncome(totalPrincipalFromCapitalizedIncome)
|
||||||
.principalAdjustments(principalAdjustments).principalPaid(principalPaid).principalWrittenOff(principalWrittenOff)
|
.principalAdjustments(principalAdjustments).principalPaid(principalPaid).principalWrittenOff(principalWrittenOff)
|
||||||
.principalOutstanding(principalOutstanding).principalOverdue(principalOverdue).interestCharged(interestCharged)
|
.principalOutstanding(principalOutstanding).principalOverdue(principalOverdue).interestCharged(interestCharged)
|
||||||
.interestPaid(interestPaid).interestWaived(interestWaived).interestWrittenOff(interestWrittenOff)
|
.interestPaid(interestPaid).interestWaived(interestWaived).interestWrittenOff(interestWrittenOff)
|
||||||
@@ -1271,15 +1281,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
private final DisbursementData disbursement;
|
private final DisbursementData disbursement;
|
||||||
private final BigDecimal totalFeeChargesDueAtDisbursement;
|
private final BigDecimal totalFeeChargesDueAtDisbursement;
|
||||||
private final Collection<DisbursementData> disbursementData;
|
private final Collection<DisbursementData> disbursementData;
|
||||||
|
private final Collection<LoanTransactionRepaymentPeriodData> capitalizedIncomeData;
|
||||||
private final LoanScheduleType loanScheduleType;
|
private final LoanScheduleType loanScheduleType;
|
||||||
private LocalDate lastDueDate;
|
private LocalDate lastDueDate;
|
||||||
private BigDecimal outstandingLoanPrincipalBalance;
|
private BigDecimal outstandingLoanPrincipalBalance;
|
||||||
private boolean excludePastUnDisbursed;
|
private boolean excludePastUnDisbursed;
|
||||||
|
|
||||||
LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
|
LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
|
||||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType) {
|
Collection<DisbursementData> disbursementData, Collection<LoanTransactionRepaymentPeriodData> capitalizedIncomeData,
|
||||||
|
boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType) {
|
||||||
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
|
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
|
||||||
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
|
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
|
||||||
|
this.capitalizedIncomeData = capitalizedIncomeData;
|
||||||
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
|
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
|
||||||
this.lastDueDate = this.disbursement.disbursementDate();
|
this.lastDueDate = this.disbursement.disbursementDate();
|
||||||
this.outstandingLoanPrincipalBalance = this.disbursement.getPrincipal();
|
this.outstandingLoanPrincipalBalance = this.disbursement.getPrincipal();
|
||||||
@@ -1359,10 +1372,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
||||||
final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
|
final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
|
||||||
final boolean complete = rs.getBoolean("complete");
|
final boolean complete = rs.getBoolean("complete");
|
||||||
BigDecimal disbursedAmount = BigDecimal.ZERO;
|
|
||||||
|
|
||||||
disbursedAmount = processDisbursementData(loanScheduleType, disbursementData, fromDate, dueDate, disbursementPeriodIds,
|
List<LoanSchedulePeriodDataWrapper> combinedDataList = new ArrayList<>();
|
||||||
disbursementChargeAmount, waivedChargeAmount, periods);
|
combinedDataList.addAll(
|
||||||
|
collectEligibleDisbursementData(loanScheduleType, disbursementData, fromDate, dueDate, disbursementPeriodIds));
|
||||||
|
combinedDataList.addAll(collectEligibleCapitalizedIncomeData(fromDate, dueDate, disbursementPeriodIds));
|
||||||
|
combinedDataList.sort(this::sortPeriodDataHolders);
|
||||||
|
fillLoanSchedulePeriodData(periods, combinedDataList, disbursementChargeAmount, waivedChargeAmount);
|
||||||
|
|
||||||
|
BigDecimal disbursedAmount = calculateDisbursedAmount(combinedDataList);
|
||||||
|
|
||||||
// Add the Charge back or Credits to the initial amount to avoid negative balance
|
// Add the Charge back or Credits to the initial amount to avoid negative balance
|
||||||
final BigDecimal principalCredits = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalCredits");
|
final BigDecimal principalCredits = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalCredits");
|
||||||
@@ -1473,26 +1491,82 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
totalPaidLate.getAmount(), totalOutstanding.getAmount(), totalCredits.getAmount());
|
totalPaidLate.getAmount(), totalOutstanding.getAmount(), totalCredits.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal processDisbursementData(LoanScheduleType loanScheduleType, Collection<DisbursementData> disbursementData,
|
private List<LoanSchedulePeriodDataWrapper> collectEligibleDisbursementData(LoanScheduleType loanScheduleType,
|
||||||
LocalDate fromDate, LocalDate dueDate, Set<Long> disbursementPeriodIds, BigDecimal disbursementChargeAmount,
|
Collection<DisbursementData> disbursementData, LocalDate fromDate, LocalDate dueDate, Set<Long> disbursementPeriodIds) {
|
||||||
BigDecimal waivedChargeAmount, List<LoanSchedulePeriodData> periods) {
|
List<LoanSchedulePeriodDataWrapper> disbursementDataList = new ArrayList<>();
|
||||||
BigDecimal disbursedAmount = BigDecimal.ZERO;
|
// Collect eligible disbursement data
|
||||||
for (final DisbursementData data : disbursementData) {
|
for (final DisbursementData data : disbursementData) {
|
||||||
boolean isDueForDisbursement = data.isDueForDisbursement(loanScheduleType, fromDate, dueDate);
|
boolean isDueForDisbursement = data.isDueForDisbursement(loanScheduleType, fromDate, dueDate);
|
||||||
if (((fromDate.equals(this.disbursement.disbursementDate()) && data.disbursementDate().equals(fromDate))
|
boolean isEligible = ((fromDate.equals(this.disbursement.disbursementDate()) && data.disbursementDate().equals(fromDate))
|
||||||
|| (fromDate.equals(dueDate) && data.disbursementDate().equals(fromDate))
|
|| (fromDate.equals(dueDate) && data.disbursementDate().equals(fromDate))
|
||||||
|| canAddDisbursementData(data, isDueForDisbursement, excludePastUnDisbursed))
|
|| canAddDisbursementData(data, isDueForDisbursement, excludePastUnDisbursed))
|
||||||
&& !disbursementPeriodIds.contains(data.getId())) {
|
&& !disbursementPeriodIds.contains(data.getId());
|
||||||
disbursedAmount = disbursedAmount.add(data.getPrincipal());
|
|
||||||
LoanSchedulePeriodData periodData = createLoanSchedulePeriodData(data, disbursementChargeAmount, waivedChargeAmount);
|
if (isEligible) {
|
||||||
periods.add(periodData);
|
disbursementDataList.add(new LoanSchedulePeriodDataWrapper(data, data.disbursementDate(), true));
|
||||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(periodData.getPrincipalDisbursed());
|
|
||||||
disbursementPeriodIds.add(data.getId());
|
disbursementPeriodIds.add(data.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return disbursementDataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<LoanSchedulePeriodDataWrapper> collectEligibleCapitalizedIncomeData(LocalDate fromDate, LocalDate dueDate,
|
||||||
|
Set<Long> disbursementPeriodIds) {
|
||||||
|
List<LoanSchedulePeriodDataWrapper> capitalizedIncomeDataList = new ArrayList<>();
|
||||||
|
// Collect eligible capitalized income data
|
||||||
|
for (LoanTransactionRepaymentPeriodData data : capitalizedIncomeData) {
|
||||||
|
boolean isEligible = canAddCapitalizedIncomeData(data, fromDate, dueDate)
|
||||||
|
&& !disbursementPeriodIds.contains(data.getTransactionId());
|
||||||
|
|
||||||
|
if (isEligible) {
|
||||||
|
capitalizedIncomeDataList.add(new LoanSchedulePeriodDataWrapper(data, data.getDate(), false));
|
||||||
|
disbursementPeriodIds.add(data.getTransactionId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return capitalizedIncomeDataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillLoanSchedulePeriodData(List<LoanSchedulePeriodData> periods, List<LoanSchedulePeriodDataWrapper> combinedDataList,
|
||||||
|
BigDecimal disbursementChargeAmount, BigDecimal waivedChargeAmount) {
|
||||||
|
// Process all collected data in chronological order
|
||||||
|
for (LoanSchedulePeriodDataWrapper dataItem : combinedDataList) {
|
||||||
|
LoanSchedulePeriodData periodData;
|
||||||
|
if (dataItem.isDisbursement()) {
|
||||||
|
// Process disbursement data
|
||||||
|
DisbursementData data = (DisbursementData) dataItem.getData();
|
||||||
|
periodData = createLoanSchedulePeriodData(data, disbursementChargeAmount, waivedChargeAmount);
|
||||||
|
} else {
|
||||||
|
// Process capitalized income data
|
||||||
|
LoanTransactionRepaymentPeriodData data = (LoanTransactionRepaymentPeriodData) dataItem.getData();
|
||||||
|
periodData = createLoanSchedulePeriodData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common processing for both data types
|
||||||
|
periods.add(periodData);
|
||||||
|
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(periodData.getPrincipalDisbursed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calculateDisbursedAmount(List<LoanSchedulePeriodDataWrapper> combinedDataList) {
|
||||||
|
BigDecimal disbursedAmount = BigDecimal.ZERO;
|
||||||
|
for (LoanSchedulePeriodDataWrapper dataItem : combinedDataList) {
|
||||||
|
if (dataItem.isDisbursement()) {
|
||||||
|
DisbursementData data = (DisbursementData) dataItem.getData();
|
||||||
|
disbursedAmount = disbursedAmount.add(data.getPrincipal());
|
||||||
|
}
|
||||||
|
}
|
||||||
return disbursedAmount;
|
return disbursedAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int sortPeriodDataHolders(LoanSchedulePeriodDataWrapper item1, LoanSchedulePeriodDataWrapper item2) {
|
||||||
|
int dateComparison = item1.getDate().compareTo(item2.getDate());
|
||||||
|
if (dateComparison == 0 && item1.isDisbursement() != item2.isDisbursement()) {
|
||||||
|
// If dates are equal, prioritize disbursement data
|
||||||
|
return item1.isDisbursement() ? -1 : 1;
|
||||||
|
}
|
||||||
|
return dateComparison;
|
||||||
|
}
|
||||||
|
|
||||||
private LoanSchedulePeriodData createLoanSchedulePeriodData(final DisbursementData data, BigDecimal disbursementChargeAmount,
|
private LoanSchedulePeriodData createLoanSchedulePeriodData(final DisbursementData data, BigDecimal disbursementChargeAmount,
|
||||||
BigDecimal waivedChargeAmount) {
|
BigDecimal waivedChargeAmount) {
|
||||||
BigDecimal chargeAmount = data.getChargeAmount() == null ? disbursementChargeAmount
|
BigDecimal chargeAmount = data.getChargeAmount() == null ? disbursementChargeAmount
|
||||||
@@ -1501,11 +1575,20 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
data.isDisbursed());
|
data.isDisbursed());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoanSchedulePeriodData createLoanSchedulePeriodData(final LoanTransactionRepaymentPeriodData data) {
|
||||||
|
BigDecimal feeCharges = Objects.isNull(data.getFeeChargesPortion()) ? BigDecimal.ZERO : data.getFeeChargesPortion();
|
||||||
|
return LoanSchedulePeriodData.disbursementOnlyPeriod(data.getDate(), data.getAmount(), feeCharges, !data.isReversed());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean canAddDisbursementData(DisbursementData data, boolean isDueForDisbursement, boolean excludePastUnDisbursed) {
|
private boolean canAddDisbursementData(DisbursementData data, boolean isDueForDisbursement, boolean excludePastUnDisbursed) {
|
||||||
return (!excludePastUnDisbursed || data.isDisbursed() || !DateUtils.isBeforeBusinessDate(data.disbursementDate()))
|
return (!excludePastUnDisbursed || data.isDisbursed() || !DateUtils.isBeforeBusinessDate(data.disbursementDate()))
|
||||||
&& isDueForDisbursement;
|
&& isDueForDisbursement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canAddCapitalizedIncomeData(LoanTransactionRepaymentPeriodData data, LocalDate fromDate, LocalDate dueDate) {
|
||||||
|
return !data.isReversed() && DateUtils.isDateInRangeFromInclusiveToExclusive(fromDate, dueDate, data.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class LoanTransactionsMapper implements RowMapper<LoanTransactionData> {
|
private static final class LoanTransactionsMapper implements RowMapper<LoanTransactionData> {
|
||||||
@@ -2168,7 +2251,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
|
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
|
||||||
final Collection<PaymentTypeData> paymentTypeOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
final Collection<PaymentTypeData> paymentTypeOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
||||||
final BigDecimal outstandingLoanBalance = loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount();
|
final BigDecimal outstandingLoanBalance = loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount();
|
||||||
final Boolean isReversed = false;
|
final Boolean isManuallyReversed = false;
|
||||||
|
|
||||||
final Money outStandingAmount = loanRepaymentScheduleInstallment.getTotalOutstanding(currency);
|
final Money outStandingAmount = loanRepaymentScheduleInstallment.getTotalOutstanding(currency);
|
||||||
|
|
||||||
@@ -2178,7 +2261,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
loanRepaymentScheduleInstallment.getInterestOutstanding(currency).getAmount(),
|
loanRepaymentScheduleInstallment.getInterestOutstanding(currency).getAmount(),
|
||||||
loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
||||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, unrecognizedIncomePortion,
|
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, unrecognizedIncomePortion,
|
||||||
paymentTypeOptions, ExternalId.empty(), null, null, outstandingLoanBalance, isReversed, loanId, loan.getExternalId());
|
paymentTypeOptions, ExternalId.empty(), null, null, outstandingLoanBalance, isManuallyReversed, loanId,
|
||||||
|
loan.getExternalId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class CurrencyMapper implements RowMapper<CurrencyData> {
|
private static final class CurrencyMapper implements RowMapper<CurrencyData> {
|
||||||
|
|||||||
+9
-2
@@ -64,6 +64,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
import org.apache.fineract.portfolio.note.domain.Note;
|
import org.apache.fineract.portfolio.note.domain.Note;
|
||||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
@@ -93,6 +94,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
private final LoanChargeValidator loanChargeValidator;
|
private final LoanChargeValidator loanChargeValidator;
|
||||||
private final LoanJournalEntryPoster journalEntryPoster;
|
private final LoanJournalEntryPoster journalEntryPoster;
|
||||||
private final LoanBalanceService loanBalanceService;
|
private final LoanBalanceService loanBalanceService;
|
||||||
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult adjustLoanTransaction(Loan loan, LoanTransaction transactionToAdjust, LoanAdjustmentParameter parameter,
|
public CommandProcessingResult adjustLoanTransaction(Loan loan, LoanTransaction transactionToAdjust, LoanAdjustmentParameter parameter,
|
||||||
@@ -245,8 +247,9 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
transactionForAdjustment.getTransactionDate());
|
transactionForAdjustment.getTransactionDate());
|
||||||
|
|
||||||
if (!transactionForAdjustment.isAccrualRelated() && transactionForAdjustment.isNotRepaymentLikeType()
|
if (!transactionForAdjustment.isAccrualRelated() && transactionForAdjustment.isNotRepaymentLikeType()
|
||||||
&& transactionForAdjustment.isNotWaiver() && transactionForAdjustment.isNotCreditBalanceRefund()) {
|
&& transactionForAdjustment.isNotWaiver() && transactionForAdjustment.isNotCreditBalanceRefund()
|
||||||
final String errorMessage = "Only (non-reversed) transactions of type repayment, waiver, accrual or credit balance refund can be adjusted.";
|
&& !transactionForAdjustment.isCapitalizedIncome()) {
|
||||||
|
final String errorMessage = "Only (non-reversed) transactions of type repayment, waiver, capitalized income, accrual or credit balance refund can be adjusted.";
|
||||||
throw new InvalidLoanTransactionTypeException("transaction",
|
throw new InvalidLoanTransactionTypeException("transaction",
|
||||||
"adjustment.is.only.allowed.to.repayment.or.waiver.or.creditbalancerefund.transactions", errorMessage);
|
"adjustment.is.only.allowed.to.repayment.or.waiver.or.creditbalancerefund.transactions", errorMessage);
|
||||||
}
|
}
|
||||||
@@ -285,6 +288,10 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, newTransactionDetail, transactionForAdjustment,
|
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, newTransactionDetail, transactionForAdjustment,
|
||||||
scheduleGeneratorDTO);
|
scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transactionForAdjustment.getTypeOf().equals(LoanTransactionType.CAPITALIZED_INCOME)) {
|
||||||
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Loan saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
private Loan saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||||
|
|||||||
+7
-3
@@ -204,9 +204,13 @@ public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createLoanNote(final Long loanId, final String note) {
|
public void createLoanTransactionNote(final Long loanTransactionId, final String note) {
|
||||||
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
|
final LoanTransaction loanTransaction = this.loanTransactionRepository.findById(loanTransactionId)
|
||||||
final Note newNote = Note.loanNote(loan, note);
|
.orElseThrow(() -> new LoanTransactionNotFoundException(loanTransactionId));
|
||||||
|
|
||||||
|
final Loan loan = loanTransaction.getLoan();
|
||||||
|
|
||||||
|
final Note newNote = Note.loanTransactionNote(loan, loanTransaction, note);
|
||||||
|
|
||||||
this.noteRepository.save(newNote);
|
this.noteRepository.save(newNote);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,4 +196,5 @@
|
|||||||
<include file="parts/0175_add_fk_acc_product_mapping.xml" relativeToChangelogFile="true" />
|
<include file="parts/0175_add_fk_acc_product_mapping.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0176_add_capitalized_income_amortization_transaction.xml" relativeToChangelogFile="true" />
|
<include file="parts/0176_add_capitalized_income_amortization_transaction.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0177_acc_journal_entry_index.xml" relativeToChangelogFile="true" />
|
<include file="parts/0177_acc_journal_entry_index.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="parts/0178_add_principal_from_capitalized_income_to_loan_summary.xml" relativeToChangelogFile="true" />
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||||
|
<changeSet author="fineract" id="1">
|
||||||
|
<addColumn tableName="m_loan">
|
||||||
|
<column defaultValueNumeric="0.000000" name="capitalized_income_derived" type="DECIMAL(19, 6)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column defaultValueNumeric="0.000000" name="total_principal_derived" type="DECIMAL(19, 6)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+23
-23
@@ -5938,7 +5938,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
executeInlineCOB(createdLoanId.get());
|
executeInlineCOB(createdLoanId.get());
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
// totalUnpaidPayableDueInterest → Total outstanding interest amount on all the periods that are before
|
// totalUnpaidPayableDueInterest → Total outstanding interest amount on all the periods that are before
|
||||||
assertEquals(BigDecimal.valueOf(loanDetails.getSummary().getInterestCharged()).stripTrailingZeros(),
|
assertEquals(loanDetails.getSummary().getInterestCharged().stripTrailingZeros(),
|
||||||
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
// Total outstanding interest amount on the current period, if the current period due date is after than the
|
// Total outstanding interest amount on the current period, if the current period due date is after than the
|
||||||
// current date
|
// current date
|
||||||
@@ -6131,21 +6131,21 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().get(index);
|
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().get(index);
|
||||||
assertEquals(dueDate, period.getDueDate());
|
assertEquals(dueDate, period.getDueDate());
|
||||||
assertEquals(paidDate, period.getObligationsMetOnDate());
|
assertEquals(paidDate, period.getObligationsMetOnDate());
|
||||||
assertEquals(balanceOfLoan, period.getPrincipalLoanBalanceOutstanding());
|
assertEquals(balanceOfLoan, Utils.getDoubleValue(period.getPrincipalLoanBalanceOutstanding()));
|
||||||
assertEquals(principalDue, period.getPrincipalDue());
|
assertEquals(principalDue, Utils.getDoubleValue(period.getPrincipalDue()));
|
||||||
assertEquals(principalPaid, period.getPrincipalPaid());
|
assertEquals(principalPaid, Utils.getDoubleValue(period.getPrincipalPaid()));
|
||||||
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
|
assertEquals(principalOutstanding, Utils.getDoubleValue(period.getPrincipalOutstanding()));
|
||||||
assertEquals(feeDue, period.getFeeChargesDue());
|
assertEquals(feeDue, Utils.getDoubleValue(period.getFeeChargesDue()));
|
||||||
assertEquals(feePaid, period.getFeeChargesPaid());
|
assertEquals(feePaid, Utils.getDoubleValue(period.getFeeChargesPaid()));
|
||||||
assertEquals(feeOutstanding, period.getFeeChargesOutstanding());
|
assertEquals(feeOutstanding, Utils.getDoubleValue(period.getFeeChargesOutstanding()));
|
||||||
assertEquals(penaltyDue, period.getPenaltyChargesDue());
|
assertEquals(penaltyDue, Utils.getDoubleValue(period.getPenaltyChargesDue()));
|
||||||
assertEquals(penaltyPaid, period.getPenaltyChargesPaid());
|
assertEquals(penaltyPaid, Utils.getDoubleValue(period.getPenaltyChargesPaid()));
|
||||||
assertEquals(penaltyOutstanding, period.getPenaltyChargesOutstanding());
|
assertEquals(penaltyOutstanding, Utils.getDoubleValue(period.getPenaltyChargesOutstanding()));
|
||||||
assertEquals(interestDue, period.getInterestDue());
|
assertEquals(interestDue, Utils.getDoubleValue(period.getInterestDue()));
|
||||||
assertEquals(interestPaid, period.getInterestPaid());
|
assertEquals(interestPaid, Utils.getDoubleValue(period.getInterestPaid()));
|
||||||
assertEquals(interestOutstanding, period.getInterestOutstanding());
|
assertEquals(interestOutstanding, Utils.getDoubleValue(period.getInterestOutstanding()));
|
||||||
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
|
assertEquals(paidInAdvance, Utils.getDoubleValue(period.getTotalPaidInAdvanceForPeriod()));
|
||||||
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
|
assertEquals(paidLate, Utils.getDoubleValue(period.getTotalPaidLateForPeriod()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
|
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
|
||||||
@@ -6181,18 +6181,18 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
|
|
||||||
private static void validateLoanTransaction(GetLoansLoanIdResponse loanDetails, int index, double transactionAmount,
|
private static void validateLoanTransaction(GetLoansLoanIdResponse loanDetails, int index, double transactionAmount,
|
||||||
double principalPortion, double overPaidPortion, double loanBalance) {
|
double principalPortion, double overPaidPortion, double loanBalance) {
|
||||||
assertEquals(transactionAmount, loanDetails.getTransactions().get(index).getAmount());
|
assertEquals(transactionAmount, Utils.getDoubleValue(loanDetails.getTransactions().get(index).getAmount()));
|
||||||
assertEquals(principalPortion, loanDetails.getTransactions().get(index).getPrincipalPortion());
|
assertEquals(principalPortion, Utils.getDoubleValue(loanDetails.getTransactions().get(index).getPrincipalPortion()));
|
||||||
assertEquals(overPaidPortion, loanDetails.getTransactions().get(index).getOverpaymentPortion());
|
assertEquals(overPaidPortion, Utils.getDoubleValue(loanDetails.getTransactions().get(index).getOverpaymentPortion()));
|
||||||
assertEquals(loanBalance, loanDetails.getTransactions().get(index).getOutstandingLoanBalance());
|
assertEquals(loanBalance, Utils.getDoubleValue(loanDetails.getTransactions().get(index).getOutstandingLoanBalance()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateLoanCharge(GetLoansLoanIdResponse loanDetails, int index, LocalDate dueDate, double charged, double paid,
|
private void validateLoanCharge(GetLoansLoanIdResponse loanDetails, int index, LocalDate dueDate, double charged, double paid,
|
||||||
double outstanding) {
|
double outstanding) {
|
||||||
GetLoansLoanIdLoanChargeData chargeData = loanDetails.getCharges().get(index);
|
GetLoansLoanIdLoanChargeData chargeData = loanDetails.getCharges().get(index);
|
||||||
assertEquals(dueDate, chargeData.getDueDate());
|
assertEquals(dueDate, chargeData.getDueDate());
|
||||||
assertEquals(charged, chargeData.getAmount());
|
assertEquals(charged, Utils.getDoubleValue(chargeData.getAmount()));
|
||||||
assertEquals(paid, chargeData.getAmountPaid());
|
assertEquals(paid, Utils.getDoubleValue(chargeData.getAmountPaid()));
|
||||||
assertEquals(outstanding, chargeData.getAmountOutstanding());
|
assertEquals(outstanding, Utils.getDoubleValue(chargeData.getAmountOutstanding()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-54
@@ -177,22 +177,22 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
||||||
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
||||||
assertEquals(dueDate, period.getDueDate());
|
assertEquals(dueDate, period.getDueDate());
|
||||||
assertEquals(principalDue, period.getPrincipalDue());
|
assertEquals(principalDue, Utils.getDoubleValue(period.getPrincipalDue()));
|
||||||
assertEquals(principalPaid, period.getPrincipalPaid());
|
assertEquals(principalPaid, Utils.getDoubleValue(period.getPrincipalPaid()));
|
||||||
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
|
assertEquals(principalOutstanding, Utils.getDoubleValue(period.getPrincipalOutstanding()));
|
||||||
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
|
assertEquals(paidInAdvance, Utils.getDoubleValue(period.getTotalPaidInAdvanceForPeriod()));
|
||||||
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
|
assertEquals(paidLate, Utils.getDoubleValue(period.getTotalPaidLateForPeriod()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, double principalDue,
|
protected static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, double principalDue,
|
||||||
double principalPaid, double principalOutstanding, double paidInAdvance, double paidLate) {
|
double principalPaid, double principalOutstanding, double paidInAdvance, double paidLate) {
|
||||||
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
||||||
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
||||||
assertEquals(principalDue, period.getPrincipalDue());
|
assertEquals(principalDue, Utils.getDoubleValue(period.getPrincipalDue()));
|
||||||
assertEquals(principalPaid, period.getPrincipalPaid());
|
assertEquals(principalPaid, Utils.getDoubleValue(period.getPrincipalPaid()));
|
||||||
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
|
assertEquals(principalOutstanding, Utils.getDoubleValue(period.getPrincipalOutstanding()));
|
||||||
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
|
assertEquals(paidInAdvance, Utils.getDoubleValue(period.getTotalPaidInAdvanceForPeriod()));
|
||||||
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
|
assertEquals(paidLate, Utils.getDoubleValue(period.getTotalPaidLateForPeriod()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void validateFullyUnpaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, String dueDate,
|
protected static void validateFullyUnpaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, String dueDate,
|
||||||
@@ -233,20 +233,20 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
||||||
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
||||||
assertEquals(dueDate, period.getDueDate());
|
assertEquals(dueDate, period.getDueDate());
|
||||||
assertEquals(principalDue, period.getPrincipalDue());
|
assertEquals(principalDue, Utils.getDoubleValue(period.getPrincipalDue()));
|
||||||
assertEquals(principalPaid, period.getPrincipalPaid());
|
assertEquals(principalPaid, Utils.getDoubleValue(period.getPrincipalPaid()));
|
||||||
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
|
assertEquals(principalOutstanding, Utils.getDoubleValue(period.getPrincipalOutstanding()));
|
||||||
assertEquals(feeDue, period.getFeeChargesDue());
|
assertEquals(feeDue, Utils.getDoubleValue(period.getFeeChargesDue()));
|
||||||
assertEquals(feePaid, period.getFeeChargesPaid());
|
assertEquals(feePaid, Utils.getDoubleValue(period.getFeeChargesPaid()));
|
||||||
assertEquals(feeOutstanding, period.getFeeChargesOutstanding());
|
assertEquals(feeOutstanding, Utils.getDoubleValue(period.getFeeChargesOutstanding()));
|
||||||
assertEquals(penaltyDue, period.getPenaltyChargesDue());
|
assertEquals(penaltyDue, Utils.getDoubleValue(period.getPenaltyChargesDue()));
|
||||||
assertEquals(penaltyPaid, period.getPenaltyChargesPaid());
|
assertEquals(penaltyPaid, Utils.getDoubleValue(period.getPenaltyChargesPaid()));
|
||||||
assertEquals(penaltyOutstanding, period.getPenaltyChargesOutstanding());
|
assertEquals(penaltyOutstanding, Utils.getDoubleValue(period.getPenaltyChargesOutstanding()));
|
||||||
assertEquals(interestDue, period.getInterestDue());
|
assertEquals(interestDue, Utils.getDoubleValue(period.getInterestDue()));
|
||||||
assertEquals(interestPaid, period.getInterestPaid());
|
assertEquals(interestPaid, Utils.getDoubleValue(period.getInterestPaid()));
|
||||||
assertEquals(interestOutstanding, period.getInterestOutstanding());
|
assertEquals(interestOutstanding, Utils.getDoubleValue(period.getInterestOutstanding()));
|
||||||
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
|
assertEquals(paidInAdvance, Utils.getDoubleValue(period.getTotalPaidInAdvanceForPeriod()));
|
||||||
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
|
assertEquals(paidLate, Utils.getDoubleValue(period.getTotalPaidLateForPeriod()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -410,6 +410,15 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
.loanScheduleProcessingType("HORIZONTAL");//
|
.loanScheduleProcessingType("HORIZONTAL");//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected PostLoanProductsRequest create4IProgressiveWithCapitalizedIncome() {
|
||||||
|
return create4IProgressive().enableIncomeCapitalization(true)//
|
||||||
|
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)//
|
||||||
|
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)//
|
||||||
|
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())//
|
||||||
|
.capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE);
|
||||||
|
}
|
||||||
|
|
||||||
// Loan product with proper accounting setup
|
// Loan product with proper accounting setup
|
||||||
protected PostLoanProductsRequest createOnePeriod30DaysPeriodicAccrualProduct(double interestRatePerPeriod) {
|
protected PostLoanProductsRequest createOnePeriod30DaysPeriodicAccrualProduct(double interestRatePerPeriod) {
|
||||||
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6))//
|
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6))//
|
||||||
@@ -615,7 +624,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size());
|
Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size());
|
||||||
Arrays.stream(transactions).forEach(tr -> {
|
Arrays.stream(transactions).forEach(tr -> {
|
||||||
Optional<GetLoansLoanIdTransactions> optTx = loanDetails.getTransactions().stream()
|
Optional<GetLoansLoanIdTransactions> optTx = loanDetails.getTransactions().stream()
|
||||||
.filter(item -> Objects.equals(item.getAmount(), tr.amount) //
|
.filter(item -> Objects.equals(Utils.getDoubleValue(item.getAmount()), tr.amount) //
|
||||||
&& Objects.equals(item.getType().getValue(), tr.type) //
|
&& Objects.equals(item.getType().getValue(), tr.type) //
|
||||||
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)))
|
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
@@ -638,16 +647,17 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
} else {
|
} else {
|
||||||
Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size(), "Number of transactions on loan " + loanId);
|
Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size(), "Number of transactions on loan " + loanId);
|
||||||
Arrays.stream(transactions).forEach(tr -> {
|
Arrays.stream(transactions).forEach(tr -> {
|
||||||
boolean found = loanDetails.getTransactions().stream().anyMatch(item -> Objects.equals(item.getAmount(), tr.amount) //
|
boolean found = loanDetails.getTransactions().stream()
|
||||||
&& Objects.equals(item.getType().getValue(), tr.type) //
|
.anyMatch(item -> Objects.equals(Utils.getDoubleValue(item.getAmount()), tr.amount) //
|
||||||
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
|
&& Objects.equals(item.getType().getValue(), tr.type) //
|
||||||
&& Objects.equals(item.getOutstandingLoanBalance(), tr.outstandingPrincipal) //
|
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
|
||||||
&& Objects.equals(item.getPrincipalPortion(), tr.principalPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getOutstandingLoanBalance()), tr.outstandingPrincipal) //
|
||||||
&& Objects.equals(item.getInterestPortion(), tr.interestPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getPrincipalPortion()), tr.principalPortion) //
|
||||||
&& Objects.equals(item.getFeeChargesPortion(), tr.feePortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getInterestPortion()), tr.interestPortion) //
|
||||||
&& Objects.equals(item.getPenaltyChargesPortion(), tr.penaltyPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getFeeChargesPortion()), tr.feePortion) //
|
||||||
&& Objects.equals(item.getOverpaymentPortion(), tr.overpaymentPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getPenaltyChargesPortion()), tr.penaltyPortion) //
|
||||||
&& Objects.equals(item.getUnrecognizedIncomePortion(), tr.unrecognizedPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getOverpaymentPortion()), tr.overpaymentPortion) //
|
||||||
|
&& Objects.equals(Utils.getDoubleValue(item.getUnrecognizedIncomePortion()), tr.unrecognizedPortion) //
|
||||||
);
|
);
|
||||||
Assertions.assertTrue(found, "Required transaction not found: " + tr + " on loan " + loanId);
|
Assertions.assertTrue(found, "Required transaction not found: " + tr + " on loan " + loanId);
|
||||||
});
|
});
|
||||||
@@ -823,8 +833,8 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
private void verifyPeriodsEquality(List<GetLoansLoanIdRepaymentPeriod> savedPeriods, List<GetLoansLoanIdRepaymentPeriod> actualPeriods,
|
private void verifyPeriodsEquality(List<GetLoansLoanIdRepaymentPeriod> savedPeriods, List<GetLoansLoanIdRepaymentPeriod> actualPeriods,
|
||||||
int startIndex, int endIndex, boolean shouldEqual) {
|
int startIndex, int endIndex, boolean shouldEqual) {
|
||||||
for (int i = startIndex; i < endIndex; i++) {
|
for (int i = startIndex; i < endIndex; i++) {
|
||||||
Double savedTotalDue = savedPeriods.get(i).getTotalDueForPeriod();
|
Double savedTotalDue = Utils.getDoubleValue(savedPeriods.get(i).getTotalDueForPeriod());
|
||||||
Double actualTotalDue = actualPeriods.get(i).getTotalDueForPeriod();
|
Double actualTotalDue = Utils.getDoubleValue(actualPeriods.get(i).getTotalDueForPeriod());
|
||||||
|
|
||||||
if (shouldEqual) {
|
if (shouldEqual) {
|
||||||
assertEquals(savedTotalDue, actualTotalDue, String.format(
|
assertEquals(savedTotalDue, actualTotalDue, String.format(
|
||||||
@@ -848,19 +858,19 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
int installmentNumber = 0;
|
int installmentNumber = 0;
|
||||||
for (int i = 0; i < installments.length; i++) {
|
for (int i = 0; i < installments.length; i++) {
|
||||||
GetLoansLoanIdRepaymentPeriod period = loanResponse.getRepaymentSchedule().getPeriods().get(i);
|
GetLoansLoanIdRepaymentPeriod period = loanResponse.getRepaymentSchedule().getPeriods().get(i);
|
||||||
Double principalDue = period.getPrincipalDue();
|
Double principalDue = Utils.getDoubleValue(period.getPrincipalDue());
|
||||||
Double amount = installments[i].principalAmount;
|
Double amount = installments[i].principalAmount;
|
||||||
|
|
||||||
if (installments[i].completed == null) { // this is for the disbursement
|
if (installments[i].completed == null) { // this is for the disbursement
|
||||||
Assertions.assertEquals(amount, period.getPrincipalLoanBalanceOutstanding(),
|
Assertions.assertEquals(amount, Utils.getDoubleValue(period.getPrincipalLoanBalanceOutstanding()),
|
||||||
"%d. installment's principal due is different, expected: %.2f, actual: %.2f".formatted(i, amount,
|
"%d. installment's principal due is different, expected: %.2f, actual: %.2f".formatted(i, amount,
|
||||||
period.getPrincipalLoanBalanceOutstanding()));
|
Utils.getDoubleValue(period.getPrincipalLoanBalanceOutstanding())));
|
||||||
} else {
|
} else {
|
||||||
Assertions.assertEquals(amount, principalDue,
|
Assertions.assertEquals(amount, principalDue,
|
||||||
"%d. installment's principal due is different, expected: %.2f, actual: %.2f".formatted(i, amount, principalDue));
|
"%d. installment's principal due is different, expected: %.2f, actual: %.2f".formatted(i, amount, principalDue));
|
||||||
|
|
||||||
Double interestAmount = installments[i].interestAmount;
|
Double interestAmount = installments[i].interestAmount;
|
||||||
Double interestDue = period.getInterestDue();
|
Double interestDue = Utils.getDoubleValue(period.getInterestDue());
|
||||||
if (interestAmount != null) {
|
if (interestAmount != null) {
|
||||||
Assertions.assertEquals(interestAmount, interestDue,
|
Assertions.assertEquals(interestAmount, interestDue,
|
||||||
"%d. installment's interest due is different, expected: %.2f, actual: %.2f".formatted(i, interestAmount,
|
"%d. installment's interest due is different, expected: %.2f, actual: %.2f".formatted(i, interestAmount,
|
||||||
@@ -868,14 +878,14 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Double feeAmount = installments[i].feeAmount;
|
Double feeAmount = installments[i].feeAmount;
|
||||||
Double feeDue = period.getFeeChargesDue();
|
Double feeDue = Utils.getDoubleValue(period.getFeeChargesDue());
|
||||||
if (feeAmount != null) {
|
if (feeAmount != null) {
|
||||||
Assertions.assertEquals(feeAmount, feeDue,
|
Assertions.assertEquals(feeAmount, feeDue,
|
||||||
"%d. installment's fee charges due is different, expected: %.2f, actual: %.2f".formatted(i, feeAmount, feeDue));
|
"%d. installment's fee charges due is different, expected: %.2f, actual: %.2f".formatted(i, feeAmount, feeDue));
|
||||||
}
|
}
|
||||||
|
|
||||||
Double penaltyAmount = installments[i].penaltyAmount;
|
Double penaltyAmount = installments[i].penaltyAmount;
|
||||||
Double penaltyDue = period.getPenaltyChargesDue();
|
Double penaltyDue = Utils.getDoubleValue(period.getPenaltyChargesDue());
|
||||||
if (penaltyAmount != null) {
|
if (penaltyAmount != null) {
|
||||||
Assertions.assertEquals(penaltyAmount, penaltyDue,
|
Assertions.assertEquals(penaltyAmount, penaltyDue,
|
||||||
"%d. installment's penalty charges due is different, expected: %.2f, actual: %.2f".formatted(i, penaltyAmount,
|
"%d. installment's penalty charges due is different, expected: %.2f, actual: %.2f".formatted(i, penaltyAmount,
|
||||||
@@ -883,7 +893,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Double outstandingAmount = installments[i].totalOutstandingAmount;
|
Double outstandingAmount = installments[i].totalOutstandingAmount;
|
||||||
Double totalOutstanding = period.getTotalOutstandingForPeriod();
|
Double totalOutstanding = Utils.getDoubleValue(period.getTotalOutstandingForPeriod());
|
||||||
if (outstandingAmount != null) {
|
if (outstandingAmount != null) {
|
||||||
Assertions.assertEquals(outstandingAmount, totalOutstanding,
|
Assertions.assertEquals(outstandingAmount, totalOutstanding,
|
||||||
"%d. installment's total outstanding is different, expected: %.2f, actual: %.2f".formatted(i, outstandingAmount,
|
"%d. installment's total outstanding is different, expected: %.2f, actual: %.2f".formatted(i, outstandingAmount,
|
||||||
@@ -893,7 +903,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
Double outstandingPrincipalExpected = installments[i].outstandingAmounts != null
|
Double outstandingPrincipalExpected = installments[i].outstandingAmounts != null
|
||||||
? installments[i].outstandingAmounts.principalOutstanding
|
? installments[i].outstandingAmounts.principalOutstanding
|
||||||
: null;
|
: null;
|
||||||
Double outstandingPrincipal = period.getPrincipalOutstanding();
|
Double outstandingPrincipal = Utils.getDoubleValue(period.getPrincipalOutstanding());
|
||||||
if (outstandingPrincipalExpected != null) {
|
if (outstandingPrincipalExpected != null) {
|
||||||
Assertions.assertEquals(outstandingPrincipalExpected, outstandingPrincipal,
|
Assertions.assertEquals(outstandingPrincipalExpected, outstandingPrincipal,
|
||||||
"%d. installment's outstanding principal is different, expected: %.2f, actual: %.2f".formatted(i,
|
"%d. installment's outstanding principal is different, expected: %.2f, actual: %.2f".formatted(i,
|
||||||
@@ -903,7 +913,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
Double outstandingFeeExpected = installments[i].outstandingAmounts != null
|
Double outstandingFeeExpected = installments[i].outstandingAmounts != null
|
||||||
? installments[i].outstandingAmounts.feeOutstanding
|
? installments[i].outstandingAmounts.feeOutstanding
|
||||||
: null;
|
: null;
|
||||||
Double outstandingFee = period.getFeeChargesOutstanding();
|
Double outstandingFee = Utils.getDoubleValue(period.getFeeChargesOutstanding());
|
||||||
if (outstandingFeeExpected != null) {
|
if (outstandingFeeExpected != null) {
|
||||||
Assertions.assertEquals(outstandingFeeExpected, outstandingFee,
|
Assertions.assertEquals(outstandingFeeExpected, outstandingFee,
|
||||||
"%d. installment's outstanding fee is different, expected: %.2f, actual: %.2f".formatted(i,
|
"%d. installment's outstanding fee is different, expected: %.2f, actual: %.2f".formatted(i,
|
||||||
@@ -913,7 +923,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
Double outstandingPenaltyExpected = installments[i].outstandingAmounts != null
|
Double outstandingPenaltyExpected = installments[i].outstandingAmounts != null
|
||||||
? installments[i].outstandingAmounts.penaltyOutstanding
|
? installments[i].outstandingAmounts.penaltyOutstanding
|
||||||
: null;
|
: null;
|
||||||
Double outstandingPenalty = period.getPenaltyChargesOutstanding();
|
Double outstandingPenalty = Utils.getDoubleValue(period.getPenaltyChargesOutstanding());
|
||||||
if (outstandingPenaltyExpected != null) {
|
if (outstandingPenaltyExpected != null) {
|
||||||
Assertions.assertEquals(outstandingPenaltyExpected, outstandingPenalty,
|
Assertions.assertEquals(outstandingPenaltyExpected, outstandingPenalty,
|
||||||
"%d. installment's outstanding penalty is different, expected: %.2f, actual: %.2f".formatted(i,
|
"%d. installment's outstanding penalty is different, expected: %.2f, actual: %.2f".formatted(i,
|
||||||
@@ -923,7 +933,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
Double outstandingTotalExpected = installments[i].outstandingAmounts != null
|
Double outstandingTotalExpected = installments[i].outstandingAmounts != null
|
||||||
? installments[i].outstandingAmounts.totalOutstanding
|
? installments[i].outstandingAmounts.totalOutstanding
|
||||||
: null;
|
: null;
|
||||||
Double outstandingTotal = period.getTotalOutstandingForPeriod();
|
Double outstandingTotal = Utils.getDoubleValue(period.getTotalOutstandingForPeriod());
|
||||||
if (outstandingTotalExpected != null) {
|
if (outstandingTotalExpected != null) {
|
||||||
Assertions.assertEquals(outstandingTotalExpected, outstandingTotal,
|
Assertions.assertEquals(outstandingTotalExpected, outstandingTotal,
|
||||||
"%d. installment's total outstanding is different, expected: %.2f, actual: %.2f".formatted(i,
|
"%d. installment's total outstanding is different, expected: %.2f, actual: %.2f".formatted(i,
|
||||||
@@ -931,7 +941,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Double loanBalanceExpected = installments[i].loanBalance;
|
Double loanBalanceExpected = installments[i].loanBalance;
|
||||||
Double loanBalance = period.getPrincipalLoanBalanceOutstanding();
|
Double loanBalance = Utils.getDoubleValue(period.getPrincipalLoanBalanceOutstanding());
|
||||||
if (loanBalanceExpected != null) {
|
if (loanBalanceExpected != null) {
|
||||||
Assertions.assertEquals(loanBalanceExpected, loanBalance,
|
Assertions.assertEquals(loanBalanceExpected, loanBalance,
|
||||||
"%d. installment's loan balance is different, expected: %.2f, actual: %.2f".formatted(i, loanBalanceExpected,
|
"%d. installment's loan balance is different, expected: %.2f, actual: %.2f".formatted(i, loanBalanceExpected,
|
||||||
@@ -1226,11 +1236,11 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
protected void validateLoanSummaryBalances(GetLoansLoanIdResponse loanDetails, Double totalOutstanding, Double totalRepayment,
|
protected void validateLoanSummaryBalances(GetLoansLoanIdResponse loanDetails, Double totalOutstanding, Double totalRepayment,
|
||||||
Double principalOutstanding, Double principalPaid, Double totalOverpaid) {
|
Double principalOutstanding, Double principalPaid, Double totalOverpaid) {
|
||||||
assertEquals(totalOutstanding, loanDetails.getSummary().getTotalOutstanding());
|
assertEquals(totalOutstanding, Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
|
||||||
assertEquals(totalRepayment, loanDetails.getSummary().getTotalRepayment());
|
assertEquals(totalRepayment, Utils.getDoubleValue(loanDetails.getSummary().getTotalRepayment()));
|
||||||
assertEquals(principalOutstanding, loanDetails.getSummary().getPrincipalOutstanding());
|
assertEquals(principalOutstanding, Utils.getDoubleValue(loanDetails.getSummary().getPrincipalOutstanding()));
|
||||||
assertEquals(principalPaid, loanDetails.getSummary().getPrincipalPaid());
|
assertEquals(principalPaid, Utils.getDoubleValue(loanDetails.getSummary().getPrincipalPaid()));
|
||||||
assertEquals(totalOverpaid, loanDetails.getTotalOverpaid());
|
assertEquals(totalOverpaid, Utils.getDoubleValue(loanDetails.getTotalOverpaid()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void checkMaturityDates(long loanId, LocalDate expectedMaturityDate, LocalDate actualMaturityDate) {
|
protected void checkMaturityDates(long loanId, LocalDate expectedMaturityDate, LocalDate actualMaturityDate) {
|
||||||
|
|||||||
+3
-1
@@ -38,6 +38,7 @@ import org.apache.fineract.client.models.PutLoansLoanIdResponse;
|
|||||||
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
|
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -619,7 +620,8 @@ public class InstallmentLevelDelinquencyAPIIntegrationTests extends BaseLoanInte
|
|||||||
.getInstallmentLevelDelinquency();
|
.getInstallmentLevelDelinquency();
|
||||||
|
|
||||||
assertThat(loan.getDelinquent().getDelinquentDays()).isEqualTo(loanLevelDelinquentDays);
|
assertThat(loan.getDelinquent().getDelinquentDays()).isEqualTo(loanLevelDelinquentDays);
|
||||||
assertThat(loan.getDelinquent().getDelinquentAmount()).isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
|
assertThat(Utils.getDoubleValue(loan.getDelinquent().getDelinquentAmount()))
|
||||||
|
.isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
|
||||||
|
|
||||||
if (expectedInstallmentLevelDelinquencyData != null && expectedInstallmentLevelDelinquencyData.length > 0) {
|
if (expectedInstallmentLevelDelinquencyData != null && expectedInstallmentLevelDelinquencyData.length > 0) {
|
||||||
assertThat(installmentLevelDelinquency).isNotNull();
|
assertThat(installmentLevelDelinquency).isNotNull();
|
||||||
|
|||||||
+15
-12
@@ -33,6 +33,7 @@ import org.apache.fineract.client.models.PostClientsResponse;
|
|||||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -468,8 +469,9 @@ public class LoanCOBCreateAccrualsTest extends BaseLoanIntegrationTest {
|
|||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
|
||||||
// No unexpected big accruals or any accrual adjustments
|
// No unexpected big accruals or any accrual adjustments
|
||||||
Assertions.assertTrue(loanDetails.getTransactions().stream().noneMatch(t -> (t.getType().getAccrual() && t.getAmount() > 0.31)
|
Assertions.assertTrue(
|
||||||
|| "loanTransactionType.accrualAdjustment".equals(t.getType().getCode())));
|
loanDetails.getTransactions().stream().noneMatch(t -> (t.getType().getAccrual() && t.getAmount().doubleValue() > 0.31)
|
||||||
|
|| "loanTransactionType.accrualAdjustment".equals(t.getType().getCode())));
|
||||||
|
|
||||||
// Accruals around installment due dates are as expected
|
// Accruals around installment due dates are as expected
|
||||||
validateTransactionsExist(loanDetails, //
|
validateTransactionsExist(loanDetails, //
|
||||||
@@ -691,16 +693,17 @@ public class LoanCOBCreateAccrualsTest extends BaseLoanIntegrationTest {
|
|||||||
|
|
||||||
private void validateTransactionsExist(GetLoansLoanIdResponse loanDetails, TransactionExt... transactions) {
|
private void validateTransactionsExist(GetLoansLoanIdResponse loanDetails, TransactionExt... transactions) {
|
||||||
Arrays.stream(transactions).forEach(tr -> {
|
Arrays.stream(transactions).forEach(tr -> {
|
||||||
boolean found = loanDetails.getTransactions().stream().anyMatch(item -> Objects.equals(item.getAmount(), tr.amount) //
|
boolean found = loanDetails.getTransactions().stream()
|
||||||
&& Objects.equals(item.getType().getValue(), tr.type) //
|
.anyMatch(item -> Objects.equals(Utils.getDoubleValue(item.getAmount()), tr.amount) //
|
||||||
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
|
&& Objects.equals(item.getType().getValue(), tr.type) //
|
||||||
&& Objects.equals(item.getOutstandingLoanBalance(), tr.outstandingPrincipal) //
|
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
|
||||||
&& Objects.equals(item.getPrincipalPortion(), tr.principalPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getOutstandingLoanBalance()), tr.outstandingPrincipal) //
|
||||||
&& Objects.equals(item.getInterestPortion(), tr.interestPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getPrincipalPortion()), tr.principalPortion) //
|
||||||
&& Objects.equals(item.getFeeChargesPortion(), tr.feePortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getInterestPortion()), tr.interestPortion) //
|
||||||
&& Objects.equals(item.getPenaltyChargesPortion(), tr.penaltyPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getFeeChargesPortion()), tr.feePortion) //
|
||||||
&& Objects.equals(item.getOverpaymentPortion(), tr.overpaymentPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getPenaltyChargesPortion()), tr.penaltyPortion) //
|
||||||
&& Objects.equals(item.getUnrecognizedIncomePortion(), tr.unrecognizedPortion) //
|
&& Objects.equals(Utils.getDoubleValue(item.getOverpaymentPortion()), tr.overpaymentPortion) //
|
||||||
|
&& Objects.equals(Utils.getDoubleValue(item.getUnrecognizedIncomePortion()), tr.unrecognizedPortion) //
|
||||||
);
|
);
|
||||||
Assertions.assertTrue(found, "Required transaction not found: " + tr + " on loan " + loanDetails.getId());
|
Assertions.assertTrue(found, "Required transaction not found: " + tr + " on loan " + loanDetails.getId());
|
||||||
});
|
});
|
||||||
|
|||||||
+3
-3
@@ -78,7 +78,7 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
|
|||||||
transaction(100.0, "Disbursement", "01 January 2024"), //
|
transaction(100.0, "Disbursement", "01 January 2024"), //
|
||||||
transaction(50.0, "Capitalized Income", "01 January 2024"), //
|
transaction(50.0, "Capitalized Income", "01 January 2024"), //
|
||||||
transaction(0.55, "Capitalized Income Amortization", "01 January 2024"), //
|
transaction(0.55, "Capitalized Income Amortization", "01 January 2024"), //
|
||||||
transaction(0.02, "Accrual", "02 January 2024"), //
|
transaction(0.03, "Accrual", "02 January 2024"), //
|
||||||
transaction(0.55, "Capitalized Income Amortization", "02 January 2024") //
|
transaction(0.55, "Capitalized Income Amortization", "02 January 2024") //
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -89,8 +89,8 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
|
|||||||
journalEntry(50, deferredIncomeLiabilityAccount, "CREDIT"), //
|
journalEntry(50, deferredIncomeLiabilityAccount, "CREDIT"), //
|
||||||
journalEntry(0.55, deferredIncomeLiabilityAccount, "DEBIT"), //
|
journalEntry(0.55, deferredIncomeLiabilityAccount, "DEBIT"), //
|
||||||
journalEntry(0.55, feeIncomeAccount, "CREDIT"), //
|
journalEntry(0.55, feeIncomeAccount, "CREDIT"), //
|
||||||
journalEntry(0.02, interestReceivableAccount, "DEBIT"), //
|
journalEntry(0.03, interestReceivableAccount, "DEBIT"), //
|
||||||
journalEntry(0.02, interestIncomeAccount, "CREDIT"), //
|
journalEntry(0.03, interestIncomeAccount, "CREDIT"), //
|
||||||
journalEntry(0.55, deferredIncomeLiabilityAccount, "DEBIT"), //
|
journalEntry(0.55, deferredIncomeLiabilityAccount, "DEBIT"), //
|
||||||
journalEntry(0.55, feeIncomeAccount, "CREDIT") //
|
journalEntry(0.55, feeIncomeAccount, "CREDIT") //
|
||||||
);
|
);
|
||||||
|
|||||||
+4
-3
@@ -27,6 +27,7 @@ import org.apache.fineract.client.models.PostLoanProductsResponse;
|
|||||||
import org.apache.fineract.client.models.PostLoansResponse;
|
import org.apache.fineract.client.models.PostLoansResponse;
|
||||||
import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
|
import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@@ -78,8 +79,8 @@ public class LoanChargeProgressiveTest extends BaseLoanIntegrationTest {
|
|||||||
final PostChargesResponse chargeResponse = createCharge(20.0d, "EUR");
|
final PostChargesResponse chargeResponse = createCharge(20.0d, "EUR");
|
||||||
addLoanCharge(loanId, chargeResponse.getResourceId(), "03 October 2024", 20.0d);
|
addLoanCharge(loanId, chargeResponse.getResourceId(), "03 October 2024", 20.0d);
|
||||||
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
Assertions.assertTrue(
|
Assertions.assertTrue(loanDetails.getTransactions().stream()
|
||||||
loanDetails.getTransactions().stream().anyMatch(t -> t.getType().getAccrual() && t.getAmount().equals(20.0d)));
|
.anyMatch(t -> t.getType().getAccrual() && Utils.getDoubleValue(t.getAmount()).equals(20.0d)));
|
||||||
});
|
});
|
||||||
runAt("04 October 2024", () -> {
|
runAt("04 October 2024", () -> {
|
||||||
globalConfigurationHelper.manageConfigurations(GlobalConfigurationConstants.ENABLE_IMMEDIATE_CHARGE_ACCRUAL_POST_MATURITY,
|
globalConfigurationHelper.manageConfigurations(GlobalConfigurationConstants.ENABLE_IMMEDIATE_CHARGE_ACCRUAL_POST_MATURITY,
|
||||||
@@ -87,7 +88,7 @@ public class LoanChargeProgressiveTest extends BaseLoanIntegrationTest {
|
|||||||
executeInlineCOB(loanId);
|
executeInlineCOB(loanId);
|
||||||
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
Assertions.assertTrue(loanDetails.getTransactions().stream()
|
Assertions.assertTrue(loanDetails.getTransactions().stream()
|
||||||
.anyMatch(t -> t.getType().getAccrual() && t.getFeeChargesPortion().equals(20.0d)));
|
.anyMatch(t -> t.getType().getAccrual() && Utils.getDoubleValue(t.getFeeChargesPortion()).equals(20.0d)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -28,6 +28,7 @@ import org.apache.fineract.client.models.PostLoanProductsRequest;
|
|||||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
|
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -231,12 +232,14 @@ public class LoanDelinquencyForNonActiveAccountsTest extends BaseLoanIntegration
|
|||||||
.getInstallmentLevelDelinquency();
|
.getInstallmentLevelDelinquency();
|
||||||
|
|
||||||
assertThat(loan.getDelinquent().getDelinquentDays()).isEqualTo(loanLevelDelinquentDays);
|
assertThat(loan.getDelinquent().getDelinquentDays()).isEqualTo(loanLevelDelinquentDays);
|
||||||
assertThat(loan.getDelinquent().getDelinquentAmount()).isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
|
assertThat(Utils.getDoubleValue(loan.getDelinquent().getDelinquentAmount()))
|
||||||
|
.isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
|
||||||
if (expectedLastRepaymentDate != null && expectedLastRepaymentAmount != null) {
|
if (expectedLastRepaymentDate != null && expectedLastRepaymentAmount != null) {
|
||||||
assertThat(loan.getDelinquent().getLastRepaymentDate()).isNotNull();
|
assertThat(loan.getDelinquent().getLastRepaymentDate()).isNotNull();
|
||||||
Assertions.assertEquals(expectedLastRepaymentDate, loan.getDelinquent().getLastRepaymentDate().format(dateTimeFormatter));
|
Assertions.assertEquals(expectedLastRepaymentDate, loan.getDelinquent().getLastRepaymentDate().format(dateTimeFormatter));
|
||||||
assertThat(loan.getDelinquent().getLastRepaymentAmount()).isNotNull();
|
assertThat(loan.getDelinquent().getLastRepaymentAmount()).isNotNull();
|
||||||
assertThat(loan.getDelinquent().getLastRepaymentAmount()).isEqualByComparingTo(Double.valueOf(expectedLastRepaymentAmount));
|
assertThat(Utils.getDoubleValue(loan.getDelinquent().getLastRepaymentAmount()))
|
||||||
|
.isEqualByComparingTo(Double.valueOf(expectedLastRepaymentAmount));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expectedInstallmentLevelDelinquencyData != null && expectedInstallmentLevelDelinquencyData.length > 0) {
|
if (expectedInstallmentLevelDelinquencyData != null && expectedInstallmentLevelDelinquencyData.length > 0) {
|
||||||
|
|||||||
+7
-6
@@ -314,7 +314,7 @@ public class LoanDisbursementDetailsIntegrationTest {
|
|||||||
getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||||
assertNotNull(getLoansLoanIdResponse);
|
assertNotNull(getLoansLoanIdResponse);
|
||||||
this.loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
|
this.loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
|
||||||
final Double limit = 2.0;
|
final BigDecimal limit = BigDecimal.TWO;
|
||||||
evaluateEqualInstallmentsForRepaymentSchedule(getLoansLoanIdResponse.getRepaymentSchedule(), limit);
|
evaluateEqualInstallmentsForRepaymentSchedule(getLoansLoanIdResponse.getRepaymentSchedule(), limit);
|
||||||
log.info("-----------MULTI DISBURSAL LOAN EQUAL INSTALLMENTS SUCCESSFULLY-------");
|
log.info("-----------MULTI DISBURSAL LOAN EQUAL INSTALLMENTS SUCCESSFULLY-------");
|
||||||
}
|
}
|
||||||
@@ -665,9 +665,9 @@ public class LoanDisbursementDetailsIntegrationTest {
|
|||||||
return collateral;
|
return collateral;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void evaluateEqualInstallmentsForRepaymentSchedule(GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule, Double limit) {
|
public void evaluateEqualInstallmentsForRepaymentSchedule(GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule, BigDecimal limit) {
|
||||||
Double totalOutstandingForPeriod = 0.0;
|
BigDecimal totalOutstandingForPeriod = BigDecimal.ZERO;
|
||||||
Double totalInstallmentAmountForPeriod = 0.0;
|
BigDecimal totalInstallmentAmountForPeriod = BigDecimal.ZERO;
|
||||||
if (getLoanRepaymentSchedule != null) {
|
if (getLoanRepaymentSchedule != null) {
|
||||||
log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
|
log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
|
||||||
for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
|
for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
|
||||||
@@ -678,8 +678,9 @@ public class LoanDisbursementDetailsIntegrationTest {
|
|||||||
totalOutstandingForPeriod = period.getTotalOutstandingForPeriod();
|
totalOutstandingForPeriod = period.getTotalOutstandingForPeriod();
|
||||||
totalInstallmentAmountForPeriod = period.getTotalInstallmentAmountForPeriod();
|
totalInstallmentAmountForPeriod = period.getTotalInstallmentAmountForPeriod();
|
||||||
} else {
|
} else {
|
||||||
assertTrue(Math.abs(period.getTotalOutstandingForPeriod() - totalOutstandingForPeriod) <= limit);
|
assertTrue(period.getTotalOutstandingForPeriod().subtract(totalOutstandingForPeriod).abs().compareTo(limit) <= 0);
|
||||||
assertTrue(Math.abs(period.getTotalInstallmentAmountForPeriod() - totalInstallmentAmountForPeriod) <= limit);
|
assertTrue(period.getTotalInstallmentAmountForPeriod().subtract(totalInstallmentAmountForPeriod).abs()
|
||||||
|
.compareTo(limit) <= 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -149,7 +149,7 @@ public class LoanRefundTransactionTest extends BaseLoanIntegrationTest {
|
|||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
Assertions.assertTrue(loanDetails.getTransactions().stream()
|
Assertions.assertTrue(loanDetails.getTransactions().stream()
|
||||||
.filter(transaction -> transaction.getType().getCode().equals("loanTransactionType.interestRefund"))
|
.filter(transaction -> transaction.getType().getCode().equals("loanTransactionType.interestRefund"))
|
||||||
.allMatch(transaction -> transaction.getAmount().compareTo(0.0) > 0));
|
.allMatch(transaction -> transaction.getAmount().doubleValue() > 0.0));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-34
@@ -110,9 +110,9 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
|
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
|
||||||
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,11 +157,11 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalPaidForPeriod()) //
|
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalPaidForPeriod())) //
|
||||||
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertEquals(expectedRepaymentAmount, summary.getTotalOutstanding());
|
assertEquals(expectedRepaymentAmount, Utils.getDoubleValue(summary.getTotalOutstanding()));
|
||||||
assertEquals(expectedDownPaymentAmount, summary.getTotalRepaymentTransaction());
|
assertEquals(expectedDownPaymentAmount, summary.getTotalRepaymentTransaction());
|
||||||
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,9 +205,9 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
|
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
|
||||||
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,12 +253,12 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedFirstDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
|
.anyMatch(period -> expectedFirstDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
|
||||||
&& expectedFirstDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedFirstDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedSecondDownPaymentAmount.equals(period.getTotalDueForPeriod())
|
.anyMatch(period -> expectedSecondDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
|
||||||
&& expectedSecondDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedSecondDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,15 +306,15 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
Double expectedTotalRepaymentAmount = expectedFirstDownPaymentAmount + expectedSecondDownPaymentAmount;
|
Double expectedTotalRepaymentAmount = expectedFirstDownPaymentAmount + expectedSecondDownPaymentAmount;
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedFirstDownPaymentAmount.equals(period.getTotalPaidForPeriod()) //
|
.anyMatch(period -> expectedFirstDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalPaidForPeriod())) //
|
||||||
&& expectedFirstDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedFirstDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedSecondDownPaymentAmount.equals(period.getTotalPaidForPeriod())
|
.anyMatch(period -> expectedSecondDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalPaidForPeriod()))
|
||||||
&& expectedSecondDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedSecondDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
||||||
assertEquals(expectedRepaymentAmount, summary.getTotalOutstanding());
|
assertEquals(expectedRepaymentAmount, Utils.getDoubleValue(summary.getTotalOutstanding()));
|
||||||
assertEquals(expectedTotalRepaymentAmount, summary.getTotalRepaymentTransaction());
|
assertEquals(expectedTotalRepaymentAmount, Utils.getDoubleValue(summary.getTotalRepaymentTransaction()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -529,14 +529,14 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
|
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
|
||||||
&& expectedDownPaymentDueDate.equals(period.getDueDate()) //
|
&& expectedDownPaymentDueDate.equals(period.getDueDate()) //
|
||||||
&& Double.valueOf(0.00).equals(period.getFeeChargesDue())));
|
&& Double.valueOf(0.00).equals(Utils.getDoubleValue(period.getFeeChargesDue()))));
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedTotalDueForRepaymentInstallment.equals(period.getTotalDueForPeriod()) //
|
.anyMatch(period -> expectedTotalDueForRepaymentInstallment.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
|
||||||
&& expectedRepaymentAmount.equals(period.getPrincipalDue()) //
|
&& expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getPrincipalDue())) //
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate()) //
|
&& expectedRepaymentDueDate.equals(period.getDueDate()) //
|
||||||
&& feeAmount.equals(period.getFeeChargesDue())));
|
&& feeAmount.equals(Utils.getDoubleValue(period.getFeeChargesDue()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -694,16 +694,16 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
|
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
|
||||||
&& expectedDownPaymentDueDate.equals(period.getDueDate()) //
|
&& expectedDownPaymentDueDate.equals(period.getDueDate()) //
|
||||||
&& Double.valueOf(0.00).equals(period.getFeeChargesDue()) //
|
&& Double.valueOf(0.00).equals(Utils.getDoubleValue(period.getFeeChargesDue())) //
|
||||||
&& Double.valueOf(0.00).equals(period.getInterestDue())));
|
&& Double.valueOf(0.00).equals(Utils.getDoubleValue(period.getInterestDue()))));
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedTotalDueForRepaymentInstallment.equals(period.getTotalDueForPeriod()) //
|
.anyMatch(period -> expectedTotalDueForRepaymentInstallment.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
|
||||||
&& expectedRepaymentAmount.equals(period.getPrincipalDue()) //
|
&& expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getPrincipalDue())) //
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate()) //
|
&& expectedRepaymentDueDate.equals(period.getDueDate()) //
|
||||||
&& feeAmount.equals(period.getFeeChargesDue()) //
|
&& feeAmount.equals(Utils.getDoubleValue(period.getFeeChargesDue())) //
|
||||||
&& Double.valueOf(10.0).equals(period.getInterestDue())));
|
&& Double.valueOf(10.0).equals(Utils.getDoubleValue(period.getInterestDue()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -864,10 +864,11 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
|
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
|
||||||
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
assertTrue(
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
|
||||||
|
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
||||||
assertNotNull(loanDetails.getDelinquencyRange());
|
assertNotNull(loanDetails.getDelinquencyRange());
|
||||||
assertEquals(2, loanDetails.getDelinquent().getDelinquentDays());
|
assertEquals(2, loanDetails.getDelinquent().getDelinquentDays());
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1662,12 +1663,13 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
|
|||||||
Double expectedTotalRepaymentTransactionAmount = 0.00;
|
Double expectedTotalRepaymentTransactionAmount = 0.00;
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
assertTrue(periods.stream() //
|
||||||
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalOutstandingForPeriod()) //
|
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalOutstandingForPeriod())) //
|
||||||
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
||||||
assertEquals(expectedTotalOutstandingAmount, summary.getTotalOutstanding());
|
assertEquals(expectedTotalOutstandingAmount, Utils.getDoubleValue(summary.getTotalOutstanding()));
|
||||||
assertEquals(expectedTotalRepaymentTransactionAmount, summary.getTotalRepaymentTransaction());
|
assertEquals(expectedTotalRepaymentTransactionAmount, summary.getTotalRepaymentTransaction());
|
||||||
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalOutstandingForPeriod())
|
assertTrue(periods.stream()
|
||||||
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
.anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalOutstandingForPeriod()))
|
||||||
|
&& expectedRepaymentDueDate.equals(period.getDueDate())));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkNoDownPaymentTransaction(final Integer loanID) {
|
private void checkNoDownPaymentTransaction(final Integer loanID) {
|
||||||
|
|||||||
+36
@@ -19,7 +19,9 @@
|
|||||||
package org.apache.fineract.integrationtests;
|
package org.apache.fineract.integrationtests;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
|
||||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansResponse;
|
import org.apache.fineract.client.models.PostLoansResponse;
|
||||||
@@ -248,4 +250,38 @@ public class LoanSummaryTest extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCapitalizedIncomeExistsInRepaymentScheduleAndModifiesPrincipal() {
|
||||||
|
runAt("01 March 2023", () -> {
|
||||||
|
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||||
|
.createLoanProduct(create4IProgressiveWithCapitalizedIncome());
|
||||||
|
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId,
|
||||||
|
loanProductsResponse.getResourceId(), "01 March 2023", 10000.00, 12.00, 4, null));
|
||||||
|
loanId = postLoansResponse.getLoanId();
|
||||||
|
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(10000.00, "01 March 2023"));
|
||||||
|
disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 March 2023");
|
||||||
|
});
|
||||||
|
runAt("02 March 2023", () -> {
|
||||||
|
loanTransactionHelper.addCapitalizedIncome(loanId, "02 March 2023", 100.00);
|
||||||
|
});
|
||||||
|
|
||||||
|
BigDecimal thousand = BigDecimal.valueOf(1000.0);
|
||||||
|
BigDecimal hundred = BigDecimal.valueOf(100.0);
|
||||||
|
BigDecimal thousandOneHundred = BigDecimal.valueOf(1100.0);
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
|
||||||
|
Assertions.assertEquals(thousand, loanDetails.getPrincipal().setScale(1, RoundingMode.HALF_UP));
|
||||||
|
Assertions.assertEquals(thousand, loanDetails.getSummary().getPrincipalDisbursed().setScale(1, RoundingMode.HALF_UP));
|
||||||
|
Assertions.assertEquals(hundred,
|
||||||
|
loanDetails.getSummary().getTotalPrincipalFromCapitalizedIncome().setScale(1, RoundingMode.HALF_UP));
|
||||||
|
Assertions.assertEquals(thousandOneHundred, loanDetails.getSummary().getTotalPrincipal().setScale(1, RoundingMode.HALF_UP));
|
||||||
|
Assertions.assertEquals(thousandOneHundred, loanDetails.getSummary().getPrincipalOutstanding().setScale(1, RoundingMode.HALF_UP));
|
||||||
|
|
||||||
|
List<GetLoansLoanIdRepaymentPeriod> periods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||||
|
Assertions.assertEquals(6, periods.size());
|
||||||
|
Assertions.assertEquals(thousand, periods.get(0).getPrincipalLoanBalanceOutstanding().setScale(1, RoundingMode.HALF_UP));
|
||||||
|
Assertions.assertEquals(hundred, periods.get(1).getPrincipalLoanBalanceOutstanding().setScale(1, RoundingMode.HALF_UP));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -46,8 +46,8 @@ public class ProgressiveLoanTrancheTest extends BaseLoanIntegrationTest {
|
|||||||
|
|
||||||
runAt("20 December 2024", () -> {
|
runAt("20 December 2024", () -> {
|
||||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024",
|
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024",
|
||||||
500.0, 7.0, 6, (request) -> request.disbursementData(
|
500.0, 7.0, 6, (request) -> request.disbursementData(List.of(new PostLoansDisbursementData()
|
||||||
List.of(new PostLoansDisbursementData().expectedDisbursementDate("20 December 2024").principal(100.0))));
|
.expectedDisbursementDate("20 December 2024").principal(BigDecimal.valueOf(100.0)))));
|
||||||
|
|
||||||
loanIdRef.set(loanId);
|
loanIdRef.set(loanId);
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ import io.restassured.response.Response;
|
|||||||
import io.restassured.specification.RequestSpecification;
|
import io.restassured.specification.RequestSpecification;
|
||||||
import io.restassured.specification.ResponseSpecification;
|
import io.restassured.specification.ResponseSpecification;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
@@ -569,4 +571,8 @@ public final class Utils {
|
|||||||
public static long getDifferenceInMonths(final LocalDate localDateBefore, final LocalDate localDateAfter) {
|
public static long getDifferenceInMonths(final LocalDate localDateBefore, final LocalDate localDateAfter) {
|
||||||
return MONTHS.between(localDateBefore, localDateAfter);
|
return MONTHS.between(localDateBefore, localDateAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Double getDoubleValue(BigDecimal amount) {
|
||||||
|
return amount == null ? null : amount.setScale(2, RoundingMode.HALF_UP).doubleValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -2688,7 +2688,7 @@ public class LoanTransactionHelper {
|
|||||||
log.info(" Id {} code {} date {} amount {}", transaction.getId(), transaction.getType().getCode(), transaction.getDate(),
|
log.info(" Id {} code {} date {} amount {}", transaction.getId(), transaction.getType().getCode(), transaction.getDate(),
|
||||||
transaction.getAmount());
|
transaction.getAmount());
|
||||||
if (transactionType.equals(transaction.getType().getCode())) {
|
if (transactionType.equals(transaction.getType().getCode())) {
|
||||||
transactionsAmount += transaction.getAmount();
|
transactionsAmount += Utils.getDoubleValue(transaction.getAmount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertEquals(amountExpected, transactionsAmount);
|
assertEquals(amountExpected, transactionsAmount);
|
||||||
@@ -2707,7 +2707,7 @@ public class LoanTransactionHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertEquals(transactionExpected, Utils.dateFormatter.format(lastTransaction.getDate()));
|
assertEquals(transactionExpected, Utils.dateFormatter.format(lastTransaction.getDate()));
|
||||||
assertEquals(amountExpected, lastTransaction.getAmount());
|
assertEquals(amountExpected, Utils.getDoubleValue(lastTransaction.getAmount()));
|
||||||
return lastTransaction.getId();
|
return lastTransaction.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2722,7 +2722,7 @@ public class LoanTransactionHelper {
|
|||||||
if (getLoansLoanIdSummary != null) {
|
if (getLoansLoanIdSummary != null) {
|
||||||
log.info("Loan with Principal Outstanding Balance {} expected {}", getLoansLoanIdSummary.getPrincipalOutstanding(),
|
log.info("Loan with Principal Outstanding Balance {} expected {}", getLoansLoanIdSummary.getPrincipalOutstanding(),
|
||||||
amountExpected);
|
amountExpected);
|
||||||
assertEquals(amountExpected, getLoansLoanIdSummary.getPrincipalOutstanding());
|
assertEquals(amountExpected, Utils.getDoubleValue(getLoansLoanIdSummary.getPrincipalOutstanding()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2730,7 +2730,7 @@ public class LoanTransactionHelper {
|
|||||||
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
||||||
if (getLoansLoanIdSummary != null) {
|
if (getLoansLoanIdSummary != null) {
|
||||||
log.info("Loan with Fees Outstanding Balance {} expected {}", getLoansLoanIdSummary.getFeeChargesOutstanding(), amountExpected);
|
log.info("Loan with Fees Outstanding Balance {} expected {}", getLoansLoanIdSummary.getFeeChargesOutstanding(), amountExpected);
|
||||||
assertEquals(amountExpected, getLoansLoanIdSummary.getFeeChargesOutstanding());
|
assertEquals(amountExpected, Utils.getDoubleValue(getLoansLoanIdSummary.getFeeChargesOutstanding()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2738,14 +2738,14 @@ public class LoanTransactionHelper {
|
|||||||
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
||||||
assertNotNull(getLoansLoanIdSummary);
|
assertNotNull(getLoansLoanIdSummary);
|
||||||
log.info("Loan with Fees Outstanding Balance {} expected {}", getLoansLoanIdSummary.getFeeChargesOutstanding(), amountExpected);
|
log.info("Loan with Fees Outstanding Balance {} expected {}", getLoansLoanIdSummary.getFeeChargesOutstanding(), amountExpected);
|
||||||
assertEquals(amountExpected, getLoansLoanIdSummary.getPenaltyChargesOutstanding());
|
assertEquals(amountExpected, Utils.getDoubleValue(getLoansLoanIdSummary.getPenaltyChargesOutstanding()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateLoanTotalOustandingBalance(GetLoansLoanIdResponse getLoansLoanIdResponse, Double amountExpected) {
|
public void validateLoanTotalOustandingBalance(GetLoansLoanIdResponse getLoansLoanIdResponse, Double amountExpected) {
|
||||||
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
|
||||||
if (getLoansLoanIdSummary != null) {
|
if (getLoansLoanIdSummary != null) {
|
||||||
log.info("Loan with Total Outstanding Balance {} expected {}", getLoansLoanIdSummary.getTotalOutstanding(), amountExpected);
|
log.info("Loan with Total Outstanding Balance {} expected {}", getLoansLoanIdSummary.getTotalOutstanding(), amountExpected);
|
||||||
assertEquals(amountExpected, getLoansLoanIdSummary.getTotalOutstanding());
|
assertEquals(amountExpected, Utils.getDoubleValue(getLoansLoanIdSummary.getTotalOutstanding()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user