FINERACT-2232: add income capitalization principal handling and repayment schedule calculations on loans
This commit is contained in:
+1
@@ -464,6 +464,7 @@ public class LoanImportHandler implements ImportHandler {
|
||||
JsonObject loanRepaymentJsonob = gsonBuilder.create().toJsonTree(loanRepayments.get(rowIndex)).getAsJsonObject();
|
||||
loanRepaymentJsonob.remove("manuallyReversed");
|
||||
loanRepaymentJsonob.remove("numberOfRepayments");
|
||||
loanRepaymentJsonob.remove("reversed");
|
||||
String payload = loanRepaymentJsonob.toString();
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.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.LoanSummaryBalancesRepository;
|
||||
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.LoanTemplateTypeRequiredException;
|
||||
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.service.LoanScheduleCalculationPlatformService;
|
||||
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.service.GLIMAccountInfoReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
|
||||
@@ -297,6 +299,7 @@ public class LoansApiResource {
|
||||
private final ClientReadPlatformService clientReadPlatformService;
|
||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||
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
|
||||
@@ -498,11 +501,13 @@ public class LoansApiResource {
|
||||
loanBasicDetails.getPageItems().forEach(i -> {
|
||||
if (i.getSummary() != null) {
|
||||
Collection<DisbursementData> disbursementData = this.loanReadPlatformService.retrieveLoanDisbursementDetails(i.getId());
|
||||
List<LoanTransactionRepaymentPeriodData> capitalizedIncomeData = this.loanCapitalizedIncomeBalanceRepository
|
||||
.findRepaymentPeriodDataByLoanId(i.getId());
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = new RepaymentScheduleRelatedLoanData(
|
||||
i.getTimeline().getExpectedDisbursementDate(), i.getTimeline().getActualDisbursementDate(), i.getCurrency(),
|
||||
i.getPrincipal(), i.getInArrearsTolerance(), i.getFeeChargesAtDisbursementCharged());
|
||||
final LoanScheduleData repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(i.getId(),
|
||||
repaymentScheduleRelatedData, disbursementData, i.isInterestRecalculationEnabled(),
|
||||
repaymentScheduleRelatedData, disbursementData, capitalizedIncomeData, i.isInterestRecalculationEnabled(),
|
||||
LoanScheduleType.fromEnumOptionData(i.getLoanScheduleType()));
|
||||
LoanSummaryDataProvider loanSummaryDataProvider = loanSummaryProviderDelegate
|
||||
.resolveLoanSummaryDataProvider(i.getTransactionProcessingStrategyCode());
|
||||
@@ -981,13 +986,15 @@ public class LoansApiResource {
|
||||
|
||||
if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
|
||||
mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName);
|
||||
List<LoanTransactionRepaymentPeriodData> capitalizedIncomeData = this.loanCapitalizedIncomeBalanceRepository
|
||||
.findRepaymentPeriodDataByLoanId(resolvedLoanId);
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = new RepaymentScheduleRelatedLoanData(
|
||||
loanBasicDetails.getTimeline().getExpectedDisbursementDate(),
|
||||
loanBasicDetails.getTimeline().getActualDisbursementDate(), loanBasicDetails.getCurrency(),
|
||||
loanBasicDetails.getPrincipal(), loanBasicDetails.getInArrearsTolerance(),
|
||||
loanBasicDetails.getFeeChargesAtDisbursementCharged());
|
||||
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(resolvedLoanId, repaymentScheduleRelatedData,
|
||||
disbursementData, loanBasicDetails.isInterestRecalculationEnabled(),
|
||||
disbursementData, capitalizedIncomeData, loanBasicDetails.isInterestRecalculationEnabled(),
|
||||
LoanScheduleType.fromEnumOptionData(loanBasicDetails.getLoanScheduleType()));
|
||||
|
||||
if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName)
|
||||
|
||||
+153
-149
@@ -42,9 +42,9 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "[2012, 4, 3]")
|
||||
public LocalDate approvalDate;
|
||||
@Schema(example = "200.000000")
|
||||
public Double approvalAmount;
|
||||
public BigDecimal approvalAmount;
|
||||
@Schema(example = "200.000000")
|
||||
public Double netDisbursalAmount;
|
||||
public BigDecimal netDisbursalAmount;
|
||||
|
||||
public GetLoanCurrency currency;
|
||||
}
|
||||
@@ -289,29 +289,29 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "30")
|
||||
public Long loanTermInDays;
|
||||
@Schema(example = "200.000000")
|
||||
public Double totalPrincipalDisbursed;
|
||||
public BigDecimal totalPrincipalDisbursed;
|
||||
@Schema(example = "200.00")
|
||||
public Double totalPrincipalExpected;
|
||||
public BigDecimal totalPrincipalExpected;
|
||||
@Schema(example = "200.00")
|
||||
public Double totalPrincipalPaid;
|
||||
public BigDecimal totalPrincipalPaid;
|
||||
@Schema(example = "0.00")
|
||||
public Double totalInterestCharged;
|
||||
public BigDecimal totalInterestCharged;
|
||||
@Schema(example = "0.00")
|
||||
public Double totalFeeChargesCharged;
|
||||
public BigDecimal totalFeeChargesCharged;
|
||||
@Schema(example = "0.00")
|
||||
public Double totalPenaltyChargesCharged;
|
||||
public BigDecimal totalPenaltyChargesCharged;
|
||||
@Schema(example = "0.00")
|
||||
public Double totalWaived;
|
||||
public BigDecimal totalWaived;
|
||||
@Schema(example = "0.00")
|
||||
public Double totalWrittenOff;
|
||||
public BigDecimal totalWrittenOff;
|
||||
@Schema(example = "200.00")
|
||||
public Double totalRepaymentExpected;
|
||||
public BigDecimal totalRepaymentExpected;
|
||||
@Schema(example = "200.00")
|
||||
public Double totalPaidInAdvance;
|
||||
public BigDecimal totalPaidInAdvance;
|
||||
@Schema(example = "0.00")
|
||||
public Double totalPaidLate;
|
||||
public BigDecimal totalPaidLate;
|
||||
@Schema(example = "0.00")
|
||||
public Double totalOutstanding;
|
||||
public BigDecimal totalOutstanding;
|
||||
public List<GetLoansLoanIdRepaymentPeriod> periods;
|
||||
}
|
||||
|
||||
@@ -332,71 +332,71 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "30")
|
||||
public Long daysInPeriod;
|
||||
@Schema(example = "200.000000")
|
||||
public Double principalOriginalDue;
|
||||
public BigDecimal principalOriginalDue;
|
||||
@Schema(example = "200.000000")
|
||||
public Double principalDue;
|
||||
public BigDecimal principalDue;
|
||||
@Schema(example = "200.000000")
|
||||
public Double principalPaid;
|
||||
public BigDecimal principalPaid;
|
||||
@Schema(example = "0.000000")
|
||||
public Double principalWrittenOff;
|
||||
public BigDecimal principalWrittenOff;
|
||||
@Schema(example = "20.000000")
|
||||
public Double principalOutstanding;
|
||||
public BigDecimal principalOutstanding;
|
||||
@Schema(example = "20.000000")
|
||||
public Double principalLoanBalanceOutstanding;
|
||||
public BigDecimal principalLoanBalanceOutstanding;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestOriginalDue;
|
||||
public BigDecimal interestOriginalDue;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestDue;
|
||||
public BigDecimal interestDue;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestPaid;
|
||||
public BigDecimal interestPaid;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestWaived;
|
||||
public BigDecimal interestWaived;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestWrittenOff;
|
||||
public BigDecimal interestWrittenOff;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestOutstanding;
|
||||
public BigDecimal interestOutstanding;
|
||||
@Schema(example = "0.000000")
|
||||
public Double feeChargesDue;
|
||||
public BigDecimal feeChargesDue;
|
||||
@Schema(example = "20.000000")
|
||||
public Double feeChargesPaid;
|
||||
public BigDecimal feeChargesPaid;
|
||||
@Schema(example = "20.000000")
|
||||
public Double feeChargesWaived;
|
||||
public BigDecimal feeChargesWaived;
|
||||
@Schema(example = "20.000000")
|
||||
public Double feeChargesWrittenOff;
|
||||
public BigDecimal feeChargesWrittenOff;
|
||||
@Schema(example = "20.000000")
|
||||
public Double feeChargesOutstanding;
|
||||
public BigDecimal feeChargesOutstanding;
|
||||
@Schema(example = "20.000000")
|
||||
public Double penaltyChargesDue;
|
||||
public BigDecimal penaltyChargesDue;
|
||||
@Schema(example = "20.000000")
|
||||
public Double penaltyChargesPaid;
|
||||
public BigDecimal penaltyChargesPaid;
|
||||
@Schema(example = "20.000000")
|
||||
public Double penaltyChargesWaived;
|
||||
public BigDecimal penaltyChargesWaived;
|
||||
@Schema(example = "20.000000")
|
||||
public Double penaltyChargesWrittenOff;
|
||||
public BigDecimal penaltyChargesWrittenOff;
|
||||
@Schema(example = "20.000000")
|
||||
public Double penaltyChargesOutstanding;
|
||||
public BigDecimal penaltyChargesOutstanding;
|
||||
@Schema(example = "20.000000")
|
||||
public Double totalOriginalDueForPeriod;
|
||||
public BigDecimal totalOriginalDueForPeriod;
|
||||
@Schema(example = "20.000000")
|
||||
public Double totalDueForPeriod;
|
||||
public BigDecimal totalDueForPeriod;
|
||||
@Schema(example = "20.000000")
|
||||
public Double totalPaidForPeriod;
|
||||
public BigDecimal totalPaidForPeriod;
|
||||
@Schema(example = "20.000000")
|
||||
public Double totalPaidInAdvanceForPeriod;
|
||||
public BigDecimal totalPaidInAdvanceForPeriod;
|
||||
@Schema(example = "20.000000")
|
||||
public Double totalPaidLateForPeriod;
|
||||
public BigDecimal totalPaidLateForPeriod;
|
||||
@Schema(example = "20.000000")
|
||||
public Double totalWaivedForPeriod;
|
||||
public BigDecimal totalWaivedForPeriod;
|
||||
@Schema(example = "20.000000")
|
||||
public Double totalWrittenOffForPeriod;
|
||||
public BigDecimal totalWrittenOffForPeriod;
|
||||
@Schema(example = "200.000000")
|
||||
public Double totalOutstandingForPeriod;
|
||||
public BigDecimal totalOutstandingForPeriod;
|
||||
@Schema(example = "20.000000")
|
||||
public Double totalActualCostOfLoanForPeriod;
|
||||
public BigDecimal totalActualCostOfLoanForPeriod;
|
||||
@Schema(example = "200.000000")
|
||||
public Double totalInstallmentAmountForPeriod;
|
||||
public BigDecimal totalInstallmentAmountForPeriod;
|
||||
@Schema(example = "2.000000")
|
||||
public Double totalCredits;
|
||||
public BigDecimal totalCredits;
|
||||
@Schema(example = "true")
|
||||
public Boolean downPaymentPeriod;
|
||||
}
|
||||
@@ -412,15 +412,15 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "[2022, 07, 01]")
|
||||
public LocalDate actualDisbursementDate;
|
||||
@Schema(example = "22000.000000")
|
||||
public Double principal;
|
||||
public BigDecimal principal;
|
||||
@Schema(example = "22000.000000")
|
||||
public Double netDisbursalAmount;
|
||||
public BigDecimal netDisbursalAmount;
|
||||
@Schema(example = "1")
|
||||
public String loanChargeId;
|
||||
@Schema(example = "22000.000000")
|
||||
public Double chargeAmount;
|
||||
public BigDecimal chargeAmount;
|
||||
@Schema(example = "22000.000000")
|
||||
public Double waivedChargeAmount;
|
||||
public BigDecimal waivedChargeAmount;
|
||||
@Schema(example = "dd MMMM yyyy")
|
||||
public String dateFormat;
|
||||
@Schema(example = "de_DE")
|
||||
@@ -517,77 +517,81 @@ final class LoansApiResourceSwagger {
|
||||
|
||||
public GetLoansLoanIdCurrency currency;
|
||||
@Schema(example = "1000000.000000")
|
||||
public Double principalDisbursed;
|
||||
@Schema(example = "0.000000")
|
||||
public Double principalPaid;
|
||||
@Schema(example = "0.00")
|
||||
public Double principalAdjustments;
|
||||
@Schema(example = "0.000000")
|
||||
public Double principalWrittenOff;
|
||||
public BigDecimal totalPrincipal;
|
||||
@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")
|
||||
public Double principalOverdue;
|
||||
public BigDecimal principalOverdue;
|
||||
@Schema(example = "240000.000000")
|
||||
public Double interestCharged;
|
||||
public BigDecimal interestCharged;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestPaid;
|
||||
public BigDecimal interestPaid;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestWaived;
|
||||
public BigDecimal interestWaived;
|
||||
@Schema(example = "0.000000")
|
||||
public Double interestWrittenOff;
|
||||
public BigDecimal interestWrittenOff;
|
||||
@Schema(example = "240000.000000")
|
||||
public Double interestOutstanding;
|
||||
public BigDecimal interestOutstanding;
|
||||
@Schema(example = "200000.000000")
|
||||
public Double interestOverdue;
|
||||
public BigDecimal interestOverdue;
|
||||
@Schema(example = "0.00")
|
||||
public Double feeAdjustments;
|
||||
public BigDecimal feeAdjustments;
|
||||
@Schema(example = "18000.000000")
|
||||
public Double feeChargesCharged;
|
||||
public BigDecimal feeChargesCharged;
|
||||
@Schema(example = "0.000000")
|
||||
public Double feeChargesDueAtDisbursementCharged;
|
||||
public BigDecimal feeChargesDueAtDisbursementCharged;
|
||||
@Schema(example = "0.000000")
|
||||
public Double feeChargesPaid;
|
||||
public BigDecimal feeChargesPaid;
|
||||
@Schema(example = "0.000000")
|
||||
public Double feeChargesWaived;
|
||||
public BigDecimal feeChargesWaived;
|
||||
@Schema(example = "0.000000")
|
||||
public Double feeChargesWrittenOff;
|
||||
public BigDecimal feeChargesWrittenOff;
|
||||
@Schema(example = "18000.000000")
|
||||
public Double feeChargesOutstanding;
|
||||
public BigDecimal feeChargesOutstanding;
|
||||
@Schema(example = "15000.000000")
|
||||
public Double feeChargesOverdue;
|
||||
public BigDecimal feeChargesOverdue;
|
||||
@Schema(example = "0.00")
|
||||
public Double penaltyAdjustments;
|
||||
public BigDecimal penaltyAdjustments;
|
||||
@Schema(example = "0.000000")
|
||||
public Double penaltyChargesCharged;
|
||||
public BigDecimal penaltyChargesCharged;
|
||||
@Schema(example = "0.000000")
|
||||
public Double penaltyChargesPaid;
|
||||
public BigDecimal penaltyChargesPaid;
|
||||
@Schema(example = "0.000000")
|
||||
public Double penaltyChargesWaived;
|
||||
public BigDecimal penaltyChargesWaived;
|
||||
@Schema(example = "0.000000")
|
||||
public Double penaltyChargesWrittenOff;
|
||||
public BigDecimal penaltyChargesWrittenOff;
|
||||
@Schema(example = "0.000000")
|
||||
public Double penaltyChargesOutstanding;
|
||||
public BigDecimal penaltyChargesOutstanding;
|
||||
@Schema(example = "0.000000")
|
||||
public Double penaltyChargesOverdue;
|
||||
public BigDecimal penaltyChargesOverdue;
|
||||
@Schema(example = "1258000.000000")
|
||||
public Double totalExpectedRepayment;
|
||||
public BigDecimal totalExpectedRepayment;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalRepayment;
|
||||
public BigDecimal totalRepayment;
|
||||
@Schema(example = "258000.000000")
|
||||
public Double totalExpectedCostOfLoan;
|
||||
public BigDecimal totalExpectedCostOfLoan;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalCostOfLoan;
|
||||
public BigDecimal totalCostOfLoan;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalWaived;
|
||||
public BigDecimal totalWaived;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalWrittenOff;
|
||||
public BigDecimal totalWrittenOff;
|
||||
@Schema(example = "1258000.000000")
|
||||
public Double totalOutstanding;
|
||||
public BigDecimal totalOutstanding;
|
||||
@Schema(example = "1048333.30000")
|
||||
public Double totalOverdue;
|
||||
public BigDecimal totalOverdue;
|
||||
@Schema(example = "2456.30000")
|
||||
public Double totalRecovered;
|
||||
public BigDecimal totalRecovered;
|
||||
@Schema(example = "[2012, 5, 10]")
|
||||
public LocalDate overdueSinceDate;
|
||||
@Schema(example = "1")
|
||||
@@ -597,9 +601,9 @@ final class LoansApiResourceSwagger {
|
||||
public GetLoansLoanIdLinkedAccount linkedAccount;
|
||||
public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
|
||||
@Schema(example = "1100.000000")
|
||||
public Double fixedEmiAmount;
|
||||
public BigDecimal fixedEmiAmount;
|
||||
@Schema(example = "35000.000000")
|
||||
public Double maxOutstandingLoanBalance;
|
||||
public BigDecimal maxOutstandingLoanBalance;
|
||||
@Schema(example = "false")
|
||||
public Boolean canDisburse;
|
||||
@Schema(example = "true")
|
||||
@@ -607,33 +611,33 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "false")
|
||||
public Boolean isNPA;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalMerchantRefund;
|
||||
public BigDecimal totalMerchantRefund;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalMerchantRefundReversed;
|
||||
public BigDecimal totalMerchantRefundReversed;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalPayoutRefund;
|
||||
public BigDecimal totalPayoutRefund;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalPayoutRefundReversed;
|
||||
public BigDecimal totalPayoutRefundReversed;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalGoodwillCredit;
|
||||
public BigDecimal totalGoodwillCredit;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalGoodwillCreditReversed;
|
||||
public BigDecimal totalGoodwillCreditReversed;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalChargeAdjustment;
|
||||
public BigDecimal totalChargeAdjustment;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalChargeAdjustmentReversed;
|
||||
public BigDecimal totalChargeAdjustmentReversed;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalChargeback;
|
||||
public BigDecimal totalChargeback;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalCreditBalanceRefund;
|
||||
public BigDecimal totalCreditBalanceRefund;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalCreditBalanceRefundReversed;
|
||||
public BigDecimal totalCreditBalanceRefundReversed;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalRepaymentTransaction;
|
||||
public BigDecimal totalRepaymentTransaction;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalInterestPaymentWaiver;
|
||||
public BigDecimal totalInterestPaymentWaiver;
|
||||
@Schema(example = "0.000000")
|
||||
public Double totalRepaymentTransactionReversed;
|
||||
public BigDecimal totalRepaymentTransactionReversed;
|
||||
@Schema(example = "0.000000")
|
||||
public BigDecimal totalUnpaidPayableDueInterest;
|
||||
@Schema(example = "0.000000")
|
||||
@@ -754,7 +758,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "11")
|
||||
public Long id;
|
||||
@Schema(example = "100.000000")
|
||||
public Double amount;
|
||||
public BigDecimal amount;
|
||||
@Schema(example = "9679")
|
||||
public Integer installmentNumber;
|
||||
@Schema(example = "1")
|
||||
@@ -810,7 +814,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "CHARGEBACK")
|
||||
public String relationType;
|
||||
@Schema(example = "100.00")
|
||||
public Double amount;
|
||||
public BigDecimal amount;
|
||||
@Schema(example = "Repayment Adjustment Chargeback")
|
||||
public String paymentType;
|
||||
|
||||
@@ -831,27 +835,27 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(description = "Payment detail")
|
||||
public GetLoansLoanIdPaymentDetailData paymentDetailData;
|
||||
@Schema(example = "100.000000")
|
||||
public Double amount;
|
||||
public BigDecimal amount;
|
||||
@Schema(example = "100.000000")
|
||||
public Double netDisbursalAmount;
|
||||
public BigDecimal netDisbursalAmount;
|
||||
@Schema(example = "100.000000")
|
||||
public Double principalPortion;
|
||||
public BigDecimal principalPortion;
|
||||
@Schema(example = "100.000000")
|
||||
public Double interestPortion;
|
||||
public BigDecimal interestPortion;
|
||||
@Schema(example = "100.000000")
|
||||
public Double feeChargesPortion;
|
||||
public BigDecimal feeChargesPortion;
|
||||
@Schema(example = "100.000000")
|
||||
public Double penaltyChargesPortion;
|
||||
public BigDecimal penaltyChargesPortion;
|
||||
@Schema(example = "100.000000")
|
||||
public Double overpaymentPortion;
|
||||
public BigDecimal overpaymentPortion;
|
||||
@Schema(example = "100.000000")
|
||||
public Double unrecognizedIncomePortion;
|
||||
public BigDecimal unrecognizedIncomePortion;
|
||||
@Schema(example = "3")
|
||||
public String externalId;
|
||||
@Schema(example = "100.000000")
|
||||
public Double fixedEmiAmount;
|
||||
public BigDecimal fixedEmiAmount;
|
||||
@Schema(example = "100.000000")
|
||||
public Double outstandingLoanBalance;
|
||||
public BigDecimal outstandingLoanBalance;
|
||||
@Schema(example = "[2022, 07, 01]")
|
||||
public LocalDate submittedOnDate;
|
||||
public boolean manuallyReversed;
|
||||
@@ -870,7 +874,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "de_DE")
|
||||
public String locale;
|
||||
@Schema(example = "100.000000")
|
||||
public Double transactionAmount;
|
||||
public BigDecimal transactionAmount;
|
||||
@Schema(example = "[2022, 07, 01]")
|
||||
public LocalDate transactionDate;
|
||||
@Schema(example = "101")
|
||||
@@ -924,19 +928,19 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "[2022, 07, 01]")
|
||||
public LocalDate dueDate;
|
||||
@Schema(example = "13.560000")
|
||||
public Double amount;
|
||||
public BigDecimal amount;
|
||||
@Schema(example = "13.560000")
|
||||
public Double amountOutstanding;
|
||||
public BigDecimal amountOutstanding;
|
||||
@Schema(example = "13.560000")
|
||||
public Double amountWaived;
|
||||
public BigDecimal amountWaived;
|
||||
@Schema(example = "false")
|
||||
public boolean paid;
|
||||
@Schema(example = "false")
|
||||
public boolean waived;
|
||||
@Schema(example = "13.560000")
|
||||
public Double amountAccrued;
|
||||
public BigDecimal amountAccrued;
|
||||
@Schema(example = "13.560000")
|
||||
public Double amountUnrecognized;
|
||||
public BigDecimal amountUnrecognized;
|
||||
}
|
||||
|
||||
@Schema(example = "3")
|
||||
@@ -952,23 +956,23 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(description = "Enum option data")
|
||||
public GetLoansLoanIdEnumOptionData chargeCalculationType;
|
||||
@Schema(example = "3.400000")
|
||||
public Double percentage;
|
||||
public BigDecimal percentage;
|
||||
@Schema(example = "13.560000")
|
||||
public Double amountPercentageAppliedTo;
|
||||
public BigDecimal amountPercentageAppliedTo;
|
||||
@Schema(description = "currency")
|
||||
public GetLoansLoanIdCurrency currency;
|
||||
@Schema(example = "102.000000")
|
||||
public Double amount;
|
||||
public BigDecimal amount;
|
||||
@Schema(example = "12.000000")
|
||||
public Double amountPaid;
|
||||
public BigDecimal amountPaid;
|
||||
@Schema(example = "14.000000")
|
||||
public Double amountWaived;
|
||||
public BigDecimal amountWaived;
|
||||
@Schema(example = "102.000000")
|
||||
public Double amountWrittenOff;
|
||||
public BigDecimal amountWrittenOff;
|
||||
@Schema(example = "102.000000")
|
||||
public Double amountOutstanding;
|
||||
public BigDecimal amountOutstanding;
|
||||
@Schema(example = "102.000000")
|
||||
public Double amountOrPercentage;
|
||||
public BigDecimal amountOrPercentage;
|
||||
@Schema(example = "false")
|
||||
public boolean penalty;
|
||||
@Schema(description = "Enum option data")
|
||||
@@ -982,15 +986,15 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "3")
|
||||
public Long loanId;
|
||||
@Schema(example = "30.000000")
|
||||
public Double minCap;
|
||||
public BigDecimal minCap;
|
||||
@Schema(example = "30.000000")
|
||||
public Double maxCap;
|
||||
public BigDecimal maxCap;
|
||||
@Schema(description = "List of GetLoansLoanIdLoanInstallmentChargeData")
|
||||
public List<GetLoansLoanIdLoanInstallmentChargeData> installmentChargeData;
|
||||
@Schema(example = "30.000000")
|
||||
private Double amountAccrued;
|
||||
private BigDecimal amountAccrued;
|
||||
@Schema(example = "30.000000")
|
||||
private Double amountUnrecognized;
|
||||
private BigDecimal amountUnrecognized;
|
||||
@Schema(example = "3ert3453")
|
||||
private String externalId;
|
||||
}
|
||||
@@ -1000,7 +1004,7 @@ final class LoansApiResourceSwagger {
|
||||
private GetLoansLoanIdDelinquencySummary() {}
|
||||
|
||||
@Schema(example = "100.000000")
|
||||
public Double availableDisbursementAmount;
|
||||
public BigDecimal availableDisbursementAmount;
|
||||
@Schema(example = "12")
|
||||
public Integer pastDueDays;
|
||||
@Schema(example = "[2022, 07, 01]")
|
||||
@@ -1010,7 +1014,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "[2022, 07, 01]")
|
||||
public LocalDate delinquentDate;
|
||||
@Schema(example = "100.000000")
|
||||
public Double delinquentAmount;
|
||||
public BigDecimal delinquentAmount;
|
||||
@Schema(example = "80.000000")
|
||||
public BigDecimal delinquentPrincipal;
|
||||
@Schema(example = "10.000000")
|
||||
@@ -1022,12 +1026,12 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "[2022, 07, 01]")
|
||||
public LocalDate lastPaymentDate;
|
||||
@Schema(example = "100.000000")
|
||||
public Double lastPaymentAmount;
|
||||
public BigDecimal lastPaymentAmount;
|
||||
|
||||
@Schema(example = "[2022, 07, 01]")
|
||||
public LocalDate lastRepaymentDate;
|
||||
@Schema(example = "100.000000")
|
||||
public Double lastRepaymentAmount;
|
||||
public BigDecimal lastRepaymentAmount;
|
||||
|
||||
@Schema(description = "List of GetLoansLoanIdDelinquencyPausePeriod")
|
||||
public List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriods;
|
||||
@@ -1094,7 +1098,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "[2024, 1, 1]")
|
||||
public LocalDate termVariationApplicableFrom;
|
||||
@Schema(example = "200.000000")
|
||||
public Double decimalValue;
|
||||
public BigDecimal decimalValue;
|
||||
@Schema(example = "[2024, 1, 1]")
|
||||
public LocalDate dateValue;
|
||||
@Schema(example = "false")
|
||||
@@ -1139,11 +1143,11 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "1000000.00")
|
||||
public BigDecimal principal;
|
||||
@Schema(example = "1000.000000")
|
||||
public Double approvedPrincipal;
|
||||
public BigDecimal approvedPrincipal;
|
||||
@Schema(example = "1001.000000")
|
||||
public Double proposedPrincipal;
|
||||
public BigDecimal proposedPrincipal;
|
||||
@Schema(example = "200.000000")
|
||||
public Double netDisbursalAmount;
|
||||
public BigDecimal netDisbursalAmount;
|
||||
@Schema(example = "12")
|
||||
public Integer termFrequency;
|
||||
public GetLoansLoanIdTermPeriodFrequencyType termPeriodFrequencyType;
|
||||
@@ -1185,7 +1189,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "false")
|
||||
public Boolean enableInstallmentLevelDelinquency;
|
||||
@Schema(example = "250.000000")
|
||||
public Double totalOverpaid;
|
||||
public BigDecimal totalOverpaid;
|
||||
public LocalDate lastClosedBusinessDate;
|
||||
@Schema(example = "[2013, 11, 1]")
|
||||
public LocalDate overpaidOnDate;
|
||||
@@ -1240,7 +1244,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "1 November 2023")
|
||||
public String expectedDisbursementDate;
|
||||
@Schema(example = "1000.00")
|
||||
public Double principal;
|
||||
public BigDecimal principal;
|
||||
}
|
||||
|
||||
private PostLoansRequest() {}
|
||||
@@ -1387,7 +1391,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "0")
|
||||
public Long totalPrincipalPaid;
|
||||
@Schema(example = "13471.52")
|
||||
public Double totalInterestCharged;
|
||||
public BigDecimal totalInterestCharged;
|
||||
@Schema(example = "0")
|
||||
public Long totalFeeChargesCharged;
|
||||
@Schema(example = "0")
|
||||
@@ -1397,7 +1401,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "0")
|
||||
public Long totalWrittenOff;
|
||||
@Schema(example = "113471.52")
|
||||
public Double totalRepaymentExpected;
|
||||
public BigDecimal totalRepaymentExpected;
|
||||
@Schema(example = "0")
|
||||
public Long totalRepayment;
|
||||
@Schema(example = "0")
|
||||
@@ -1616,7 +1620,7 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "[2012, 4, 3]")
|
||||
public LocalDate expectedDisbursementDate;
|
||||
@Schema(example = "22000")
|
||||
public Double principal;
|
||||
public BigDecimal principal;
|
||||
}
|
||||
|
||||
@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())
|
||||
.principalDisbursed(defaultSummaryData.getPrincipalDisbursed())
|
||||
.principalDisbursed(defaultSummaryData.getPrincipalDisbursed()).totalPrincipal(defaultSummaryData.getTotalPrincipal())
|
||||
.totalPrincipalFromCapitalizedIncome(defaultSummaryData.getTotalPrincipalFromCapitalizedIncome())
|
||||
.principalAdjustments(defaultSummaryData.getPrincipalAdjustments()).principalPaid(defaultSummaryData.getPrincipalPaid())
|
||||
.principalWrittenOff(defaultSummaryData.getPrincipalWrittenOff())
|
||||
.principalOutstanding(defaultSummaryData.getPrincipalOutstanding())
|
||||
|
||||
+19
-9
@@ -21,10 +21,12 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
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.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
@@ -51,9 +53,10 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
@Transactional
|
||||
public void processCapitalizedIncomeAmortizationOnLoanClosure(@NotNull final Loan loan) {
|
||||
final LocalDate transactionDate = getFinalCapitalizedIncomeAmortizationTransactionDate(loan);
|
||||
final LoanTransaction amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate, false);
|
||||
businessEventNotifierService
|
||||
.notifyPostBusinessEvent(new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(amortizationTransaction));
|
||||
final Optional<LoanTransaction> amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate,
|
||||
false);
|
||||
amortizationTransaction.ifPresent(loanTransaction -> businessEventNotifierService
|
||||
.notifyPostBusinessEvent(new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(loanTransaction)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,13 +70,16 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
transactionDate = DateUtils.getBusinessLocalDate();
|
||||
}
|
||||
|
||||
final LoanTransaction amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate, true);
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
businessEventNotifierService
|
||||
.notifyPostBusinessEvent(new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(amortizationTransaction));
|
||||
final Optional<LoanTransaction> amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate,
|
||||
true);
|
||||
if (amortizationTransaction.isPresent()) {
|
||||
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) {
|
||||
ExternalId externalId = ExternalId.empty();
|
||||
BigDecimal totalUnrecognizedAmount = BigDecimal.ZERO;
|
||||
@@ -92,13 +98,17 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
balance.setUnrecognizedAmount(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
if (MathUtil.isLessThanOrEqualZero(totalUnrecognizedAmount)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final LoanTransaction amortizationTransaction = LoanTransaction.capitalizedIncomeAmortization(loan, loan.getOffice(),
|
||||
transactionDate, totalUnrecognizedAmount, externalId);
|
||||
|
||||
loan.addLoanTransaction(amortizationTransaction);
|
||||
loanTransactionRepository.saveAndFlush(amortizationTransaction);
|
||||
|
||||
return amortizationTransaction;
|
||||
return Optional.of(amortizationTransaction);
|
||||
}
|
||||
|
||||
@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.LoanInterestRecalculationData;
|
||||
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.LoanSummaryData;
|
||||
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.LoanSubStatus;
|
||||
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.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
@@ -249,8 +251,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
accountData.getFeeChargesAtDisbursementCharged());
|
||||
|
||||
final Collection<DisbursementData> disbursementData = retrieveLoanDisbursementDetails(accountData.getId());
|
||||
List<LoanTransactionRepaymentPeriodData> capitalizedIncomeData = loanCapitalizedIncomeBalanceRepository
|
||||
.findRepaymentPeriodDataByLoanId(accountData.getId());
|
||||
final LoanScheduleData repaymentSchedule = retrieveRepaymentSchedule(accountData.getId(), repaymentScheduleRelatedData,
|
||||
disbursementData, accountData.isInterestRecalculationEnabled(),
|
||||
disbursementData, capitalizedIncomeData, accountData.isInterestRecalculationEnabled(),
|
||||
LoanScheduleType.fromEnumOptionData(accountData.getLoanScheduleType()));
|
||||
accountData.setRepaymentSchedule(repaymentSchedule);
|
||||
return accountData;
|
||||
@@ -259,13 +263,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
@Override
|
||||
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData,
|
||||
boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType) {
|
||||
Collection<LoanTransactionRepaymentPeriodData> capitalizedIncomeData, boolean isInterestRecalculationEnabled,
|
||||
LoanScheduleType loanScheduleType) {
|
||||
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
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";
|
||||
|
||||
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.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.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.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, "
|
||||
@@ -764,7 +770,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
+ sqlGenerator.escape("name")
|
||||
+ " 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.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.fee_adjustments_derived as feeAdjustments, l.penalty_adjustments_derived as penaltyAdjustments,"
|
||||
+ " l.principal_outstanding_derived as principalOutstanding, l.interest_charged_derived as interestCharged,"
|
||||
@@ -1023,6 +1029,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
|
||||
// loan summary
|
||||
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 principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
|
||||
final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff");
|
||||
@@ -1066,6 +1075,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
inArrears = (overdueSinceDate != null);
|
||||
|
||||
loanSummary = LoanSummaryData.builder().currency(currencyData).principalDisbursed(principalDisbursed)
|
||||
.totalPrincipal(totalPrincipal).totalPrincipalFromCapitalizedIncome(totalPrincipalFromCapitalizedIncome)
|
||||
.principalAdjustments(principalAdjustments).principalPaid(principalPaid).principalWrittenOff(principalWrittenOff)
|
||||
.principalOutstanding(principalOutstanding).principalOverdue(principalOverdue).interestCharged(interestCharged)
|
||||
.interestPaid(interestPaid).interestWaived(interestWaived).interestWrittenOff(interestWrittenOff)
|
||||
@@ -1271,15 +1281,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
private final DisbursementData disbursement;
|
||||
private final BigDecimal totalFeeChargesDueAtDisbursement;
|
||||
private final Collection<DisbursementData> disbursementData;
|
||||
private final Collection<LoanTransactionRepaymentPeriodData> capitalizedIncomeData;
|
||||
private final LoanScheduleType loanScheduleType;
|
||||
private LocalDate lastDueDate;
|
||||
private BigDecimal outstandingLoanPrincipalBalance;
|
||||
private boolean excludePastUnDisbursed;
|
||||
|
||||
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.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
|
||||
this.capitalizedIncomeData = capitalizedIncomeData;
|
||||
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
|
||||
this.lastDueDate = this.disbursement.disbursementDate();
|
||||
this.outstandingLoanPrincipalBalance = this.disbursement.getPrincipal();
|
||||
@@ -1359,10 +1372,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
||||
final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
|
||||
final boolean complete = rs.getBoolean("complete");
|
||||
BigDecimal disbursedAmount = BigDecimal.ZERO;
|
||||
|
||||
disbursedAmount = processDisbursementData(loanScheduleType, disbursementData, fromDate, dueDate, disbursementPeriodIds,
|
||||
disbursementChargeAmount, waivedChargeAmount, periods);
|
||||
List<LoanSchedulePeriodDataWrapper> combinedDataList = new ArrayList<>();
|
||||
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
|
||||
final BigDecimal principalCredits = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalCredits");
|
||||
@@ -1473,26 +1491,82 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
totalPaidLate.getAmount(), totalOutstanding.getAmount(), totalCredits.getAmount());
|
||||
}
|
||||
|
||||
private BigDecimal processDisbursementData(LoanScheduleType loanScheduleType, Collection<DisbursementData> disbursementData,
|
||||
LocalDate fromDate, LocalDate dueDate, Set<Long> disbursementPeriodIds, BigDecimal disbursementChargeAmount,
|
||||
BigDecimal waivedChargeAmount, List<LoanSchedulePeriodData> periods) {
|
||||
BigDecimal disbursedAmount = BigDecimal.ZERO;
|
||||
private List<LoanSchedulePeriodDataWrapper> collectEligibleDisbursementData(LoanScheduleType loanScheduleType,
|
||||
Collection<DisbursementData> disbursementData, LocalDate fromDate, LocalDate dueDate, Set<Long> disbursementPeriodIds) {
|
||||
List<LoanSchedulePeriodDataWrapper> disbursementDataList = new ArrayList<>();
|
||||
// Collect eligible disbursement data
|
||||
for (final DisbursementData data : disbursementData) {
|
||||
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))
|
||||
|| canAddDisbursementData(data, isDueForDisbursement, excludePastUnDisbursed))
|
||||
&& !disbursementPeriodIds.contains(data.getId())) {
|
||||
disbursedAmount = disbursedAmount.add(data.getPrincipal());
|
||||
LoanSchedulePeriodData periodData = createLoanSchedulePeriodData(data, disbursementChargeAmount, waivedChargeAmount);
|
||||
periods.add(periodData);
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(periodData.getPrincipalDisbursed());
|
||||
&& !disbursementPeriodIds.contains(data.getId());
|
||||
|
||||
if (isEligible) {
|
||||
disbursementDataList.add(new LoanSchedulePeriodDataWrapper(data, data.disbursementDate(), true));
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
BigDecimal waivedChargeAmount) {
|
||||
BigDecimal chargeAmount = data.getChargeAmount() == null ? disbursementChargeAmount
|
||||
@@ -1501,11 +1575,20 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
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) {
|
||||
return (!excludePastUnDisbursed || data.isDisbursed() || !DateUtils.isBeforeBusinessDate(data.disbursementDate()))
|
||||
&& 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> {
|
||||
@@ -2168,7 +2251,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
|
||||
final Collection<PaymentTypeData> paymentTypeOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
||||
final BigDecimal outstandingLoanBalance = loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount();
|
||||
final Boolean isReversed = false;
|
||||
final Boolean isManuallyReversed = false;
|
||||
|
||||
final Money outStandingAmount = loanRepaymentScheduleInstallment.getTotalOutstanding(currency);
|
||||
|
||||
@@ -2178,7 +2261,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
loanRepaymentScheduleInstallment.getInterestOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
||||
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> {
|
||||
|
||||
+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.LoanJournalEntryPoster;
|
||||
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.NoteRepository;
|
||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
@@ -93,6 +94,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanJournalEntryPoster journalEntryPoster;
|
||||
private final LoanBalanceService loanBalanceService;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult adjustLoanTransaction(Loan loan, LoanTransaction transactionToAdjust, LoanAdjustmentParameter parameter,
|
||||
@@ -245,8 +247,9 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
transactionForAdjustment.getTransactionDate());
|
||||
|
||||
if (!transactionForAdjustment.isAccrualRelated() && transactionForAdjustment.isNotRepaymentLikeType()
|
||||
&& transactionForAdjustment.isNotWaiver() && transactionForAdjustment.isNotCreditBalanceRefund()) {
|
||||
final String errorMessage = "Only (non-reversed) transactions of type repayment, waiver, accrual or credit balance refund can be adjusted.";
|
||||
&& transactionForAdjustment.isNotWaiver() && transactionForAdjustment.isNotCreditBalanceRefund()
|
||||
&& !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",
|
||||
"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,
|
||||
scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
if (transactionForAdjustment.getTypeOf().equals(LoanTransactionType.CAPITALIZED_INCOME)) {
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
}
|
||||
|
||||
private Loan saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
|
||||
+7
-3
@@ -204,9 +204,13 @@ public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatf
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLoanNote(final Long loanId, final String note) {
|
||||
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
|
||||
final Note newNote = Note.loanNote(loan, note);
|
||||
public void createLoanTransactionNote(final Long loanTransactionId, final String note) {
|
||||
final LoanTransaction loanTransaction = this.loanTransactionRepository.findById(loanTransactionId)
|
||||
.orElseThrow(() -> new LoanTransactionNotFoundException(loanTransactionId));
|
||||
|
||||
final Loan loan = loanTransaction.getLoan();
|
||||
|
||||
final Note newNote = Note.loanTransactionNote(loan, loanTransaction, note);
|
||||
|
||||
this.noteRepository.save(newNote);
|
||||
}
|
||||
|
||||
@@ -196,4 +196,5 @@
|
||||
<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/0177_acc_journal_entry_index.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0178_add_principal_from_capitalized_income_to_loan_summary.xml" relativeToChangelogFile="true" />
|
||||
</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>
|
||||
Reference in New Issue
Block a user