+76
-43
@@ -6839,18 +6839,10 @@ An enumeration that indicates the type of transaction processing strategy to be
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class=alt>
|
<tr class=alt>
|
||||||
<td>recalculationFrequencyType</td>
|
<td>recalculationRestFrequencyDate</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=fielddesc>Frequency at which Outstanding Principal is recalculated. This field is mandatory when interest interest recalculation is enabled for product.
|
<td class=fielddesc>Specifies rest frequency start date for interest recalculation. This date must be before or equal to disbursement date
|
||||||
<br>Available calculation types:
|
|
||||||
<ol>
|
|
||||||
<li>1 = Same as repayment period</li>
|
|
||||||
<li>2 = Daily</li>
|
|
||||||
<li>3 = Weekly</li>
|
|
||||||
<li>4 = Fortnightly</li>
|
|
||||||
<li>5 = Monthly</li>
|
|
||||||
</ol>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -7024,7 +7016,12 @@ GET https://DomainName/api/v1/loans/template?templateType=individual&clientId=1&
|
|||||||
"id": 2,
|
"id": 2,
|
||||||
"code": "loanRescheduleStrategyMethod.reduce.number.of.installments",
|
"code": "loanRescheduleStrategyMethod.reduce.number.of.installments",
|
||||||
"value": "Reduce number of installments"
|
"value": "Reduce number of installments"
|
||||||
}
|
},
|
||||||
|
"recalculationRestFrequencyType": {
|
||||||
|
"id":1,
|
||||||
|
"code":"interestRecalculationFrequencyType.same.as.repayment.period",
|
||||||
|
"value":"Same as repayment period"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"charges": [],
|
"charges": [],
|
||||||
"productOptions": [
|
"productOptions": [
|
||||||
@@ -7215,32 +7212,7 @@ GET https://DomainName/api/v1/loans/template?templateType=individual&clientId=1&
|
|||||||
"fieldOfficerId":0,
|
"fieldOfficerId":0,
|
||||||
"currency":{"code":"USD","name":"US Dollar","decimalPlaces":2,"displaySymbol":"$","nameCode":"currency.USD","displayLabel":"US Dollar ($)"}
|
"currency":{"code":"USD","name":"US Dollar","decimalPlaces":2,"displaySymbol":"$","nameCode":"currency.USD","displayLabel":"US Dollar ($)"}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"interestRecalculationFrequencyTypeOptions": [{
|
|
||||||
"id": 1,
|
|
||||||
"code": "interestRecalculationFrequencyType.same.as.repayment.period",
|
|
||||||
"value": "Same as repayment period"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"code": "interestRecalculationFrequencyType.daily",
|
|
||||||
"value": "Daily"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"code": "interestRecalculationFrequencyType.weekly",
|
|
||||||
"value": "Weekly"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"code": "interestRecalculationFrequencyType.fortnightly",
|
|
||||||
"value": "Fortnightly"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"code": "interestRecalculationFrequencyType.monthly",
|
|
||||||
"value": "Monthly"
|
|
||||||
}],
|
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
@@ -7271,6 +7243,12 @@ GET https://DomainName/api/v1/loans/template?templateType=individual&clientId=1&
|
|||||||
<dd>
|
<dd>
|
||||||
<b>'repaymentSchedule':</b> Loan schedule data.
|
<b>'repaymentSchedule':</b> Loan schedule data.
|
||||||
</dd>
|
</dd>
|
||||||
|
<dd>
|
||||||
|
<b>'originalSchedule':</b> Loan schedule data without interest recalculations.
|
||||||
|
</dd>
|
||||||
|
<dd>
|
||||||
|
<b>'futureSchedule':</b> Loan schedule data from today date(will be displayed only for interest first repayment strategy processors)
|
||||||
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<b>'transactions':</b> Loan transactions data.
|
<b>'transactions':</b> Loan transactions data.
|
||||||
</dd>
|
</dd>
|
||||||
@@ -7980,10 +7958,10 @@ Request Body:
|
|||||||
<br/>
|
<br/>
|
||||||
<table class=matrixHeading>
|
<table class=matrixHeading>
|
||||||
<tr class="matrixHeadingBG">
|
<tr class="matrixHeadingBG">
|
||||||
<td><div class="mifosXHeading2">Additional Mandatory Fields if interest recalculation is enabled for product</div></td>
|
<td><div class="mifosXHeading2">Additional Mandatory Fields if interest recalculation is enabled for product and Rest frequency not same as repayment period</div></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class=alt>
|
<tr class=alt>
|
||||||
<td>recalculationFrequencyType</td>
|
<td>recalculationRestFrequencyDate</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<br/>
|
<br/>
|
||||||
@@ -8027,7 +8005,6 @@ Request Body:
|
|||||||
"submittedOnDate": "10 Jun 2013",
|
"submittedOnDate": "10 Jun 2013",
|
||||||
"linkAccountId" : "1",
|
"linkAccountId" : "1",
|
||||||
"fixedEmiAmount":1100,
|
"fixedEmiAmount":1100,
|
||||||
"recalculationFrequencyType":"1",
|
|
||||||
"maxOutstandingLoanBalance":"35000",
|
"maxOutstandingLoanBalance":"35000",
|
||||||
"disbursementData":[{"expectedDisbursementDate":"01 November 2013",
|
"disbursementData":[{"expectedDisbursementDate":"01 November 2013",
|
||||||
"principal":22000,"approvedPrincipal":22000}]
|
"principal":22000,"approvedPrincipal":22000}]
|
||||||
@@ -10874,6 +10851,25 @@ An enumeration that indicates the type of transaction processing strategy to be
|
|||||||
<td class=fielddesc>Specifies what action should perform on loan repayment schedule for advance payments. <br>
|
<td class=fielddesc>Specifies what action should perform on loan repayment schedule for advance payments. <br>
|
||||||
<span>Example Values:</span>1=Reschedule next repayments, 2=Reduce number of installments, 3=Reduce EMI amount</td>
|
<span>Example Values:</span>1=Reschedule next repayments, 2=Reduce number of installments, 3=Reduce EMI amount</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>recalculationRestFrequencyType</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Specifies effective date from which the late or advanced payment amounts will be considered in recalculation.<br>
|
||||||
|
<span>Example Values:</span>1=Same as repayment period, 2=Daily, 3=Weekly, 4=Monthly</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>recalculationRestFrequencyInterval</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Specifies rest frequency interval for interest recalculation.<br>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>recalculationRestFrequencyDate</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Specifies rest frequency start date for interest recalculation.<br>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -10983,7 +10979,12 @@ GET https://DomainName/api/v1/loanproducts
|
|||||||
"id": 2,
|
"id": 2,
|
||||||
"code": "loanRescheduleStrategyMethod.reduce.number.of.installments",
|
"code": "loanRescheduleStrategyMethod.reduce.number.of.installments",
|
||||||
"value": "Reduce number of installments"
|
"value": "Reduce number of installments"
|
||||||
}
|
},
|
||||||
|
"recalculationRestFrequencyType": {
|
||||||
|
"id":1,
|
||||||
|
"code":"interestRecalculationFrequencyType.same.as.repayment.period",
|
||||||
|
"value":"Same as repayment period"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"accountingRule": {
|
"accountingRule": {
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@@ -11831,7 +11832,28 @@ GET https://DomainName/api/v1/loanproducts/template
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"code": "loanRescheduleStrategyMethod.reschedule.next.repayments",
|
"code": "loanRescheduleStrategyMethod.reschedule.next.repayments",
|
||||||
"value": "Reschedule next repayments"
|
"value": "Reschedule next repayments"
|
||||||
}]
|
}],
|
||||||
|
"interestRecalculationFrequencyTypeOptions": [
|
||||||
|
{
|
||||||
|
"id":1,
|
||||||
|
"code":"interestRecalculationFrequencyType.same.as.repayment.period",
|
||||||
|
"value":"Same as repayment period"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":2,
|
||||||
|
"code":"interestRecalculationFrequencyType.daily",
|
||||||
|
"value":"Daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":3,
|
||||||
|
"code":"interestRecalculationFrequencyType.weekly",
|
||||||
|
"value":"Weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":4,
|
||||||
|
"code":"interestRecalculationFrequencyType.monthly",
|
||||||
|
"value":"Monthly"
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
@@ -12120,7 +12142,15 @@ GET https://DomainName/api/v1/loanproducts/{productId}
|
|||||||
<td><div class="mifosXHeading2">Additional Mandatory Fields if interest recalculation is enabled(true)</div></td>
|
<td><div class="mifosXHeading2">Additional Mandatory Fields if interest recalculation is enabled(true)</div></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class=alt>
|
<tr class=alt>
|
||||||
<td>interestRecalculationCompoundingMethod,rescheduleStrategyMethod</td>
|
<td>interestRecalculationCompoundingMethod, rescheduleStrategyMethod, recalculationRestFrequencyType</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Additional Optional Fields if interest recalculation is enabled(true) and recalculationRestFrequencyType is not same as repayment period</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>recalculationRestFrequencyInterval, recalculationRestFrequencyDate</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -12219,6 +12249,7 @@ Request Body:
|
|||||||
"isInterestRecalculationEnabled":"true",
|
"isInterestRecalculationEnabled":"true",
|
||||||
"interestRecalculationCompoundingMethod":"0",
|
"interestRecalculationCompoundingMethod":"0",
|
||||||
"rescheduleStrategyMethod":"3",
|
"rescheduleStrategyMethod":"3",
|
||||||
|
"recalculationRestFrequencyType":"1",
|
||||||
"accountingRule":"2",
|
"accountingRule":"2",
|
||||||
"fundSourceAccountId":"4",
|
"fundSourceAccountId":"4",
|
||||||
"loanPortfolioAccountId":"8",
|
"loanPortfolioAccountId":"8",
|
||||||
@@ -12341,6 +12372,7 @@ Request Body:
|
|||||||
"isInterestRecalculationEnabled":"true",
|
"isInterestRecalculationEnabled":"true",
|
||||||
"interestRecalculationCompoundingMethod":"0",
|
"interestRecalculationCompoundingMethod":"0",
|
||||||
"rescheduleStrategyMethod":"3",
|
"rescheduleStrategyMethod":"3",
|
||||||
|
"recalculationRestFrequencyType":"1"
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
<code class="method-response">
|
<code class="method-response">
|
||||||
@@ -12351,6 +12383,7 @@ Request Body:
|
|||||||
"isInterestRecalculationEnabled":"true",
|
"isInterestRecalculationEnabled":"true",
|
||||||
"interestRecalculationCompoundingMethod":"0",
|
"interestRecalculationCompoundingMethod":"0",
|
||||||
"rescheduleStrategyMethod":"3",
|
"rescheduleStrategyMethod":"3",
|
||||||
|
"recalculationRestFrequencyType":"1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|||||||
+1
-2
@@ -365,8 +365,7 @@ public class LoansApiResource {
|
|||||||
this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId);
|
this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (associationParameters.contains("originalSchedule") && loanBasicDetails.isInterestRecalculationEnabled()
|
if (associationParameters.contains("originalSchedule") && loanBasicDetails.isInterestRecalculationEnabled()) {
|
||||||
&& loanBasicDetails.isInterestRecalculationEnabled()) {
|
|
||||||
mandatoryResponseParameters.add("originalSchedule");
|
mandatoryResponseParameters.add("originalSchedule");
|
||||||
LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule(
|
LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule(
|
||||||
loanId, repaymentScheduleRelatedData, disbursementData);
|
loanId, repaymentScheduleRelatedData, disbursementData);
|
||||||
|
|||||||
+20
-15
@@ -1820,24 +1820,11 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
|
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
|
||||||
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
|
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
|
||||||
|
|
||||||
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
|
||||||
boolean isEmiAmountChanged = false;
|
|
||||||
if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
|
||||||
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
|
||||||
actualDisbursementDate.toDate(), emiAmount, this);
|
|
||||||
this.loanTermVariations.add(loanVariationTerms);
|
|
||||||
isEmiAmountChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||||
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
|
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
|
||||||
LocalDate recalculateFrom = null;
|
LocalDate recalculateFrom = null;
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||||
} else if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule
|
|
||||||
|| isEmiAmountChanged) {
|
|
||||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
|
||||||
workingDays);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
@@ -1863,6 +1850,24 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void regenerateScheduleOnDisbursement(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||||
|
final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
|
||||||
|
final List<Holiday> holidays, final WorkingDays workingDays, final boolean recalculateSchedule,
|
||||||
|
final LocalDate actualDisbursementDate, BigDecimal emiAmount) {
|
||||||
|
boolean isEmiAmountChanged = false;
|
||||||
|
if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
||||||
|
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
||||||
|
actualDisbursementDate.toDate(), emiAmount, this);
|
||||||
|
this.loanTermVariations.add(loanVariationTerms);
|
||||||
|
isEmiAmountChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
|
||||||
|
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||||
|
workingDays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean canDisburse(final LocalDate actualDisbursementDate) {
|
public boolean canDisburse(final LocalDate actualDisbursementDate) {
|
||||||
Date lastDusburseDate = this.actualDisbursementDate;
|
Date lastDusburseDate = this.actualDisbursementDate;
|
||||||
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
|
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
|
||||||
@@ -1991,7 +1996,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return isDisbursementMissed;
|
return isDisbursementMissed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal getDisbursedAmount() {
|
public BigDecimal getDisbursedAmount() {
|
||||||
BigDecimal principal = BigDecimal.ZERO;
|
BigDecimal principal = BigDecimal.ZERO;
|
||||||
for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
|
for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
|
||||||
if (disbursementDetail.actualDisbursementDate() != null) {
|
if (disbursementDetail.actualDisbursementDate() != null) {
|
||||||
@@ -4282,7 +4287,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate());
|
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate());
|
||||||
|
|
||||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
|
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
|
||||||
LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc);
|
LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc, charges());
|
||||||
} else {
|
} else {
|
||||||
installment = this.getTotalOutstandingOnLoan();
|
installment = this.getTotalOutstandingOnLoan();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -436,7 +436,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||||
Money waivedFeeChargesPortionOfTransaction = Money.zero(currency);
|
Money waivedFeeChargesPortionOfTransaction = Money.zero(currency);
|
||||||
|
|
||||||
final Money feesDue = getPenaltyChargesOutstanding(currency);
|
final Money feesDue = getFeeChargesOutstanding(currency);
|
||||||
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feesDue)) {
|
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feesDue)) {
|
||||||
this.feeChargesWaived = getFeeChargesWaived(currency).plus(feesDue).getAmount();
|
this.feeChargesWaived = getFeeChargesWaived(currency).plus(feesDue).getAmount();
|
||||||
waivedFeeChargesPortionOfTransaction = waivedFeeChargesPortionOfTransaction.plus(feesDue);
|
waivedFeeChargesPortionOfTransaction = waivedFeeChargesPortionOfTransaction.plus(feesDue);
|
||||||
|
|||||||
+5
@@ -301,4 +301,9 @@ public final class LoanSummary {
|
|||||||
public BigDecimal getTotalPenaltyChargesCharged() {
|
public BigDecimal getTotalPenaltyChargesCharged() {
|
||||||
return this.totalPenaltyChargesCharged;
|
return this.totalPenaltyChargesCharged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BigDecimal getTotalPrincipalWrittenOff() {
|
||||||
|
return this.totalPrincipalWrittenOff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+4
-2
@@ -451,11 +451,13 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
transactionDate = recalculationDates.get(transactionDate);
|
transactionDate = recalculationDates.get(transactionDate);
|
||||||
}
|
}
|
||||||
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||||
newLoanTransaction.resetDerivedComponents();
|
if (newLoanTransaction.isRepayment() || newLoanTransaction.isInterestWaiver() || newLoanTransaction.isRecoveryRepayment()) {
|
||||||
|
newLoanTransaction.resetDerivedComponents();
|
||||||
|
}
|
||||||
Money unProcessed = processTransaction(newLoanTransaction, currency, installments, amountToProcess);
|
Money unProcessed = processTransaction(newLoanTransaction, currency, installments, amountToProcess);
|
||||||
if (transactionDate.isAfter(installment.getFromDate()) && transactionDate.isBefore(installment.getDueDate())) {
|
if (transactionDate.isAfter(installment.getFromDate()) && transactionDate.isBefore(installment.getDueDate())) {
|
||||||
Money earlyPayment = currentInstallmentOutstanding.minus(installment.getTotalOutstanding(currency));
|
Money earlyPayment = currentInstallmentOutstanding.minus(installment.getTotalOutstanding(currency));
|
||||||
if (earlyPayment.isGreaterThanZero()) {
|
if (earlyPayment.isGreaterThanZero() && (newLoanTransaction.isRepayment() || newLoanTransaction.isInterestWaiver() || newLoanTransaction.isRecoveryRepayment())) {
|
||||||
earlypaymentDetail.add(new RecalculationDetail(false, transactionDate, null, earlyPayment, false));
|
earlypaymentDetail.add(new RecalculationDetail(false, transactionDate, null, earlyPayment, false));
|
||||||
}
|
}
|
||||||
currentInstallmentOutstanding = installment.getTotalOutstanding(currency);
|
currentInstallmentOutstanding = installment.getTotalOutstanding(currency);
|
||||||
|
|||||||
+97
-64
@@ -53,12 +53,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||||
final List<RecalculationDetail> diffAmt = null;
|
final List<RecalculationDetail> diffAmt = null;
|
||||||
return generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays, diffAmt);
|
final LocalDate prepayDate = null;
|
||||||
|
return generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays, diffAmt,
|
||||||
|
prepayDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanScheduleModel generate(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
private LoanScheduleModel generate(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays, List<RecalculationDetail> diffAmt) {
|
final List<Holiday> holidays, final WorkingDays workingDays, List<RecalculationDetail> diffAmt, LocalDate prepayDate) {
|
||||||
|
|
||||||
// 1. generate list of proposed schedule due dates
|
// 1. generate list of proposed schedule due dates
|
||||||
final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays,
|
final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays,
|
||||||
@@ -191,8 +193,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||||
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
|
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
|
||||||
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
|
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
|
||||||
disburseAmt = disbursementForPeriod(loanApplicationTerms, periodStartDate, scheduledDueDate, disbursementDatas,
|
LocalDate tillDate = scheduledDueDate;
|
||||||
diffAmt != null);
|
if (prepayDate != null && prepayDate.isBefore(tillDate)) {
|
||||||
|
tillDate = prepayDate;
|
||||||
|
}
|
||||||
|
disburseAmt = disbursementForPeriod(loanApplicationTerms, periodStartDate, tillDate, disbursementDatas, diffAmt != null);
|
||||||
principalDisbursed = principalDisbursed.plus(disburseAmt);
|
principalDisbursed = principalDisbursed.plus(disburseAmt);
|
||||||
loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseAmt));
|
loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseAmt));
|
||||||
outstandingBalance = outstandingBalance.plus(disburseAmt);
|
outstandingBalance = outstandingBalance.plus(disburseAmt);
|
||||||
@@ -207,11 +212,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement(
|
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement(
|
||||||
disbursementData.disbursementDate(), disbursedAmt, chargesDueAtTimeOfDisbursement);
|
disbursementData.disbursementDate(), disbursedAmt, chargesDueAtTimeOfDisbursement);
|
||||||
periods.add(disbursementPeriod);
|
periods.add(disbursementPeriod);
|
||||||
if (disbursementData.disbursementDate().isAfter(periodStartDateApplicableForInterest)) {
|
|
||||||
|
if (disbursementData.disbursementDate().isAfter(periodStartDateApplicableForInterest)
|
||||||
|
&& (prepayDate == null || prepayDate.isAfter(disbursementData.disbursementDate()) || disbursementData
|
||||||
|
.isDisbursed())) {
|
||||||
interestToBeAdded = interestToBeAdded.add(calculateInterestForSpecificDays(mc, loanApplicationTerms, periodNumber,
|
interestToBeAdded = interestToBeAdded.add(calculateInterestForSpecificDays(mc, loanApplicationTerms, periodNumber,
|
||||||
totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableInFullInstallment,
|
totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableInFullInstallment,
|
||||||
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, disbursementData.disbursementDate(),
|
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, disbursementData.disbursementDate(),
|
||||||
scheduledDueDate));
|
tillDate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,14 +232,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
case REDUCE_EMI_AMOUNT:
|
case REDUCE_EMI_AMOUNT:
|
||||||
loanApplicationTerms.setFixedEmiAmount(null);
|
loanApplicationTerms.setFixedEmiAmount(null);
|
||||||
outstandingBalance = outstandingBalance.minus(reducePrincipal);
|
outstandingBalance = outstandingBalance.minus(reducePrincipal);
|
||||||
balanceForcalculation = outstandingBalance;
|
balanceForcalculation = balanceForcalculation.minus(reducePrincipal);
|
||||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
|
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
|
||||||
reducePrincipal = reducePrincipal.zero();
|
reducePrincipal = reducePrincipal.zero();
|
||||||
break;
|
break;
|
||||||
case REDUCE_NUMBER_OF_INSTALLMENTS:
|
case REDUCE_NUMBER_OF_INSTALLMENTS:
|
||||||
loanApplicationTerms.setFixedEmiAmount(fixedEmiAmount.getAmount());
|
loanApplicationTerms.setFixedEmiAmount(fixedEmiAmount.getAmount());
|
||||||
outstandingBalance = outstandingBalance.minus(reducePrincipal);
|
outstandingBalance = outstandingBalance.minus(reducePrincipal);
|
||||||
balanceForcalculation = outstandingBalance;
|
balanceForcalculation = balanceForcalculation.minus(reducePrincipal);
|
||||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
|
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
|
||||||
reducePrincipal = reducePrincipal.zero();
|
reducePrincipal = reducePrincipal.zero();
|
||||||
break;
|
break;
|
||||||
@@ -1022,11 +1030,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
List<LoanRepaymentScheduleInstallment> installments = previousSchedule;
|
List<LoanRepaymentScheduleInstallment> installments = previousSchedule;
|
||||||
List<LoanRepaymentScheduleInstallment> removeInstallments = new ArrayList<>();
|
List<LoanRepaymentScheduleInstallment> removeInstallments = new ArrayList<>();
|
||||||
MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency();
|
MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency();
|
||||||
LoanTransaction preCloseTransaction = getPreclosureTransaction(transactions, installments, currency, loanApplicationTerms, mc);
|
LoanTransaction preCloseTransaction = getPreclosureTransaction(transactions, installments, currency, loanApplicationTerms, mc,
|
||||||
|
loanCharges);
|
||||||
|
Integer updatedInstallmentNumber = 1;
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
if (installment.isRecalculatedInterestComponent() && recalculateFrom != null
|
if (installment.isRecalculatedInterestComponent() && recalculateFrom != null
|
||||||
&& !recalculateFrom.isAfter(installment.getDueDate())) {
|
&& !recalculateFrom.isAfter(installment.getDueDate())) {
|
||||||
removeInstallments.add(installment);
|
removeInstallments.add(installment);
|
||||||
|
} else {
|
||||||
|
installment.updateInstallmentNumber(updatedInstallmentNumber++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
installments.removeAll(removeInstallments);
|
installments.removeAll(removeInstallments);
|
||||||
@@ -1097,12 +1109,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
final List<RecalculationDetail> recalculationDetails, final Map<LocalDate, RecalculationDetail> retainRecalculationDetails,
|
final List<RecalculationDetail> recalculationDetails, final Map<LocalDate, RecalculationDetail> retainRecalculationDetails,
|
||||||
boolean recalculate, int installmentNumber, List<LoanTransaction> processTransactions, LoanTransaction preCloseTransaction) {
|
boolean recalculate, int installmentNumber, List<LoanTransaction> processTransactions, LoanTransaction preCloseTransaction) {
|
||||||
LoanTransaction loanTransaction = processTransactions.remove(processTransactions.size() - 1);
|
LoanTransaction loanTransaction = processTransactions.remove(processTransactions.size() - 1);
|
||||||
if (loanTransaction == preCloseTransaction) {
|
if (!(loanTransaction.isRepayment() || loanTransaction.isInterestWaiver())) {
|
||||||
processTransactions.add(loanTransaction);
|
processTransactions.add(loanTransaction);
|
||||||
return loanScheduleModel;
|
return loanScheduleModel;
|
||||||
}
|
}
|
||||||
LocalDate processTransactionsForInterestCompound = getNextRestScheduleDate(loanTransaction.getTransactionDate().minusDays(1),
|
LocalDate processTransactionsForInterestCompound = loanTransaction.getTransactionDate();
|
||||||
loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
if (loanTransaction != preCloseTransaction) {
|
||||||
|
processTransactionsForInterestCompound = getNextRestScheduleDate(loanTransaction.getTransactionDate().minusDays(1),
|
||||||
|
loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
||||||
|
}
|
||||||
List<LoanRepaymentScheduleInstallment> scheduleInstallments = getInstallmentsForInterestCompound(installments,
|
List<LoanRepaymentScheduleInstallment> scheduleInstallments = getInstallmentsForInterestCompound(installments,
|
||||||
processTransactionsForInterestCompound, retainRecalculationDetails.keySet());
|
processTransactionsForInterestCompound, retainRecalculationDetails.keySet());
|
||||||
|
|
||||||
@@ -1214,7 +1229,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final List<RecalculationDetail> recalculationDetails,
|
final List<LoanRepaymentScheduleInstallment> installments, final List<RecalculationDetail> recalculationDetails,
|
||||||
final int skipRecalculation, LoanTransaction preCloseTransaction) {
|
final int skipRecalculation, final LoanTransaction preCloseTransaction) {
|
||||||
boolean processRecalculate = false;
|
boolean processRecalculate = false;
|
||||||
int processedInstallmentNumber = installmentNumber;
|
int processedInstallmentNumber = installmentNumber;
|
||||||
|
|
||||||
@@ -1324,8 +1339,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
LoanScheduleModel model = null;
|
LoanScheduleModel model = null;
|
||||||
recalculationDetails.addAll(diffAmt);
|
recalculationDetails.addAll(diffAmt);
|
||||||
if (processRecalculate) {
|
if (processRecalculate) {
|
||||||
|
LocalDate prepayDate = null;
|
||||||
|
if (preCloseTransaction != null) {
|
||||||
|
prepayDate = preCloseTransaction.getTransactionDate();
|
||||||
|
}
|
||||||
model = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays,
|
model = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays,
|
||||||
recalculationDetails);
|
recalculationDetails, prepayDate);
|
||||||
}
|
}
|
||||||
return new RecalculatedSchedule(model, processedInstallmentNumber);
|
return new RecalculatedSchedule(model, processedInstallmentNumber);
|
||||||
}
|
}
|
||||||
@@ -1392,7 +1411,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
@Override
|
@Override
|
||||||
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(final List<LoanRepaymentScheduleInstallment> installments,
|
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(final List<LoanRepaymentScheduleInstallment> installments,
|
||||||
MonetaryCurrency currency, final LocalDate onDate, LocalDate interestChargedFromLocalDate,
|
MonetaryCurrency currency, final LocalDate onDate, LocalDate interestChargedFromLocalDate,
|
||||||
LoanApplicationTerms loanApplicationTerms, MathContext mc) {
|
LoanApplicationTerms loanApplicationTerms, MathContext mc, Set<LoanCharge> charges) {
|
||||||
Money feeCharges = Money.zero(currency);
|
Money feeCharges = Money.zero(currency);
|
||||||
Money penaltyCharges = Money.zero(currency);
|
Money penaltyCharges = Money.zero(currency);
|
||||||
Money totalPrincipal = Money.zero(currency);
|
Money totalPrincipal = Money.zero(currency);
|
||||||
@@ -1400,12 +1419,21 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
LocalDate calculateInterestFrom = onDate;
|
LocalDate calculateInterestFrom = onDate;
|
||||||
LocalDate dueDate = onDate;
|
LocalDate dueDate = onDate;
|
||||||
LocalDate repaymentDueDate = null;
|
LocalDate repaymentDueDate = null;
|
||||||
|
LocalDate periodStartDate = onDate;
|
||||||
boolean isInterestRepayment = false;
|
boolean isInterestRepayment = false;
|
||||||
Money inerestForCurrentInstallment = Money.zero(currency);
|
Money inerestForCurrentInstallment = Money.zero(currency);
|
||||||
Money principalOutstanding = Money.zero(currency);
|
Money principalOutstanding = Money.zero(currency);
|
||||||
Integer periodNumber = 0;
|
Integer periodNumber = 0;
|
||||||
LocalDate lastDueDate = loanApplicationTerms.getExpectedDisbursementDate();
|
LocalDate lastDueDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||||
|
if (!currentInstallment.getDueDate().isAfter(LocalDate.now())) {
|
||||||
|
calculateInterestFrom = currentInstallment.getFromDate();
|
||||||
|
if (currentInstallment.isRecalculatedInterestComponent()) {
|
||||||
|
isInterestRepayment = true;
|
||||||
|
} else {
|
||||||
|
isInterestRepayment = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (currentInstallment.isNotFullyPaidOff()) {
|
if (currentInstallment.isNotFullyPaidOff()) {
|
||||||
if (!currentInstallment.getDueDate().isAfter(onDate)) {
|
if (!currentInstallment.getDueDate().isAfter(onDate)) {
|
||||||
totalPrincipal = totalPrincipal.plus(currentInstallment.getPrincipalOutstanding(currency));
|
totalPrincipal = totalPrincipal.plus(currentInstallment.getPrincipalOutstanding(currency));
|
||||||
@@ -1417,8 +1445,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
totalPrincipal = totalPrincipal.minus(currentInstallment.getPrincipalCompleted(currency));
|
totalPrincipal = totalPrincipal.minus(currentInstallment.getPrincipalCompleted(currency));
|
||||||
totalInterest = totalInterest.minus(currentInstallment.getInterestPaid(currency)).minus(
|
totalInterest = totalInterest.minus(currentInstallment.getInterestPaid(currency)).minus(
|
||||||
currentInstallment.getInterestWaived(currency));
|
currentInstallment.getInterestWaived(currency));
|
||||||
if (currentInstallment.getFromDate().isBefore(calculateInterestFrom)) {
|
if (currentInstallment.getFromDate().isBefore(periodStartDate)) {
|
||||||
calculateInterestFrom = currentInstallment.getFromDate();
|
if (!isInterestRepayment) {
|
||||||
|
calculateInterestFrom = currentInstallment.getFromDate();
|
||||||
|
}
|
||||||
|
periodStartDate = currentInstallment.getFromDate();
|
||||||
dueDate = currentInstallment.getDueDate();
|
dueDate = currentInstallment.getDueDate();
|
||||||
inerestForCurrentInstallment = currentInstallment.getInterestCharged(currency);
|
inerestForCurrentInstallment = currentInstallment.getInterestCharged(currency);
|
||||||
if (currentInstallment.isRecalculatedInterestComponent()) {
|
if (currentInstallment.isRecalculatedInterestComponent()) {
|
||||||
@@ -1443,8 +1474,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
calculateInterestFrom = interestChargedFromLocalDate;
|
calculateInterestFrom = interestChargedFromLocalDate;
|
||||||
}
|
}
|
||||||
Money interest = Money.zero(currency);
|
Money interest = Money.zero(currency);
|
||||||
|
LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(
|
||||||
if (calculateInterestFrom.isBefore(onDate)) {
|
loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, true);
|
||||||
|
final LocalDate idealDisbursementDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(
|
||||||
|
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate);
|
||||||
|
if (calculateInterestFrom.isBefore(onDate) && !periodStartDate.isBefore(idealDisbursementDate)) {
|
||||||
boolean useDailyInterest = false;
|
boolean useDailyInterest = false;
|
||||||
if (repaymentDueDate == null) {
|
if (repaymentDueDate == null) {
|
||||||
if (isInterestRepayment) {
|
if (isInterestRepayment) {
|
||||||
@@ -1453,7 +1487,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
repaymentDueDate = dueDate;
|
repaymentDueDate = dueDate;
|
||||||
}
|
}
|
||||||
int daysInPeriodApplicableForInterest = Days.daysBetween(calculateInterestFrom, repaymentDueDate).getDays();
|
int daysInPeriodApplicableForInterest = Days.daysBetween(calculateInterestFrom, repaymentDueDate).getDays();
|
||||||
int days = Days.daysBetween(calculateInterestFrom, onDate).getDays();
|
int days = Days.daysBetween(periodStartDate, onDate).getDays();
|
||||||
|
int actualPeriodDays = Days.daysBetween(periodStartDate, repaymentDueDate).getDays();
|
||||||
BigDecimal interestForInstallment = BigDecimal.ZERO;
|
BigDecimal interestForInstallment = BigDecimal.ZERO;
|
||||||
if (useDailyInterest) {
|
if (useDailyInterest) {
|
||||||
interestForInstallment = loanApplicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc,
|
interestForInstallment = loanApplicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc,
|
||||||
@@ -1462,11 +1497,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
interest = interest.plus(calculateInterestForDays(daysInPeriodApplicableForInterest, interestForInstallment, days));
|
interest = interest.plus(calculateInterestForDays(daysInPeriodApplicableForInterest, interestForInstallment, days));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(
|
|
||||||
loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, true);
|
|
||||||
final LocalDate idealDisbursementDate = this.scheduledDateGenerator
|
|
||||||
.idealDisbursementDateBasedOnFirstRepaymentDate(loanApplicationTerms.getLoanTermPeriodFrequencyType(),
|
|
||||||
loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate);
|
|
||||||
LocalDate periodStartDateApplicableForInterest = calculateInterestFrom;
|
LocalDate periodStartDateApplicableForInterest = calculateInterestFrom;
|
||||||
if (calculateInterestFrom.isBefore(idealDisbursementDate)) {
|
if (calculateInterestFrom.isBefore(idealDisbursementDate)) {
|
||||||
if (loanApplicationTerms.getInterestChargedFromLocalDate() != null) {
|
if (loanApplicationTerms.getInterestChargedFromLocalDate() != null) {
|
||||||
@@ -1481,58 +1512,48 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, repaymentDueDate,
|
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, repaymentDueDate,
|
||||||
loanApplicationTerms.getInterestChargedFromLocalDate(),
|
loanApplicationTerms.getInterestChargedFromLocalDate(),
|
||||||
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
|
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
|
||||||
|
Money calculateInterestOnPrincipal = principalOutstanding;
|
||||||
|
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
|
||||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||||
BigDecimal reducePrincipal = disbursementAfterPeriod(loanApplicationTerms, repaymentDueDate, lastDueDate);
|
BigDecimal reducePrincipal = disbursementAfterPeriod(loanApplicationTerms, repaymentDueDate, lastDueDate);
|
||||||
principalOutstanding = principalOutstanding.minus(reducePrincipal);
|
principalOutstanding = principalOutstanding.minus(reducePrincipal);
|
||||||
|
calculateInterestOnPrincipal = principalOutstanding;
|
||||||
|
BigDecimal currentPeriodDisbursal = disbursementForPeriod(loanApplicationTerms, periodStartDate, repaymentDueDate,
|
||||||
|
disbursementDatas, true);
|
||||||
|
calculateInterestOnPrincipal = calculateInterestOnPrincipal.minus(currentPeriodDisbursal);
|
||||||
}
|
}
|
||||||
|
|
||||||
PrincipalInterest principalInterest = loanApplicationTerms.calculateTotalInterestForPeriod(
|
PrincipalInterest principalInterest = loanApplicationTerms.calculateTotalInterestForPeriod(
|
||||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc,
|
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc,
|
||||||
totalOutstandingInterestPaymentDueToGrace.zero(), daysInPeriodApplicableForInterest, principalOutstanding);
|
totalOutstandingInterestPaymentDueToGrace.zero(), daysInPeriodApplicableForInterest, calculateInterestOnPrincipal);
|
||||||
interestForInstallment = principalInterest.interest().getAmount();
|
interestForInstallment = BigDecimal.valueOf(calculateInterestForDays(daysInPeriodApplicableForInterest, principalInterest
|
||||||
Money alteredPrincipal = principalOutstanding;
|
.interest().getAmount(), actualPeriodDays));
|
||||||
Money reducePricipal = principalOutstanding.zero();
|
BigDecimal interestToBeAdded = BigDecimal.ZERO;
|
||||||
BigDecimal interestToBeReduced = BigDecimal.ZERO;
|
BigDecimal interestToBeReduced = BigDecimal.ZERO;
|
||||||
BigDecimal interestForExtraPrincipal = BigDecimal.ZERO;
|
|
||||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||||
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
|
|
||||||
BigDecimal addedPrincipal = disbursementForPeriod(loanApplicationTerms, calculateInterestFrom, repaymentDueDate,
|
|
||||||
disbursementDatas, true);
|
|
||||||
alteredPrincipal = alteredPrincipal.minus(addedPrincipal);
|
|
||||||
for (DisbursementData disbursementData : disbursementDatas) {
|
for (DisbursementData disbursementData : disbursementDatas) {
|
||||||
Money disbursedAmt = Money.of(currency, disbursementData.amount());
|
Money disbursedAmt = Money.of(currency, disbursementData.amount());
|
||||||
if (disbursementData.disbursementDate().isAfter(calculateInterestFrom)) {
|
LocalDate calculateTill = disbursementData.disbursementDate();
|
||||||
LocalDate calculateTill = disbursementData.disbursementDate();
|
if (!onDate.isAfter(calculateTill)) {
|
||||||
|
principalOutstanding = principalOutstanding.minus(disbursedAmt);
|
||||||
|
} else {
|
||||||
|
interestToBeAdded = interestToBeAdded.add(calculateInterestForSpecificDays(mc, loanApplicationTerms,
|
||||||
|
periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
|
||||||
|
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, calculateTill, onDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disbursementData.disbursementDate().isAfter(periodStartDateApplicableForInterest)) {
|
||||||
interestToBeReduced = interestToBeReduced.add(calculateInterestForSpecificDays(mc, loanApplicationTerms,
|
interestToBeReduced = interestToBeReduced.add(calculateInterestForSpecificDays(mc, loanApplicationTerms,
|
||||||
periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
|
periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
|
||||||
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, calculateInterestFrom, calculateTill));
|
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, calculateTill, repaymentDueDate));
|
||||||
|
|
||||||
if (onDate.isAfter(calculateTill)) {
|
|
||||||
interestForExtraPrincipal = interestForExtraPrincipal.add(calculateInterestForSpecificDays(mc,
|
|
||||||
loanApplicationTerms, periodNumber, totalOutstandingInterestPaymentDueToGrace,
|
|
||||||
daysInPeriodApplicableForInterest, interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt,
|
|
||||||
calculateTill, onDate));
|
|
||||||
} else {
|
|
||||||
reducePricipal = reducePricipal.plus(disbursedAmt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment).plus(interestToBeReduced));
|
|
||||||
BigDecimal interestForCloseDate = BigDecimal.ZERO;
|
|
||||||
if (alteredPrincipal.isNotEqualTo(principalOutstanding)) {
|
|
||||||
interestForCloseDate = interestForExtraPrincipal;
|
|
||||||
interestForCloseDate = interestForCloseDate.add(calculateInterestForSpecificDays(mc, loanApplicationTerms,
|
|
||||||
periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
|
|
||||||
interestCalculationGraceOnRepaymentPeriodFraction, alteredPrincipal, calculateInterestFrom, onDate));
|
|
||||||
} else {
|
|
||||||
interestForCloseDate = interestForCloseDate.add(BigDecimal.valueOf(calculateInterestForDays(
|
|
||||||
daysInPeriodApplicableForInterest, interestForInstallment, days)));
|
|
||||||
}
|
|
||||||
interest = interest.plus(interestForCloseDate);
|
|
||||||
principalOutstanding = principalOutstanding.minus(reducePricipal);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment).minus(interestToBeReduced));
|
||||||
|
BigDecimal interestForCloseDate = BigDecimal.valueOf(calculateInterestForDays(actualPeriodDays, interestForInstallment,
|
||||||
|
days));
|
||||||
|
interest = interest.plus(interestForCloseDate).plus(interestToBeAdded);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1540,6 +1561,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
totalInterest = totalInterest.plus(interest);
|
totalInterest = totalInterest.plus(interest);
|
||||||
totalPrincipal = totalPrincipal.plus(principalOutstanding);
|
totalPrincipal = totalPrincipal.plus(principalOutstanding);
|
||||||
|
|
||||||
|
for (LoanCharge loanCharge : charges) {
|
||||||
|
if (loanCharge.isActive() && loanCharge.isNotFullyPaid() && !loanCharge.isWaived()
|
||||||
|
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStartDate, onDate)) {
|
||||||
|
if (loanCharge.isFeeCharge()) {
|
||||||
|
feeCharges = feeCharges.plus(loanCharge.amountOutstanding());
|
||||||
|
} else {
|
||||||
|
penaltyCharges = penaltyCharges.plus(loanCharge.amountOutstanding());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new LoanRepaymentScheduleInstallment(null, 0, onDate, onDate, totalPrincipal.getAmount(), totalInterest.getAmount(),
|
return new LoanRepaymentScheduleInstallment(null, 0, onDate, onDate, totalPrincipal.getAmount(), totalInterest.getAmount(),
|
||||||
feeCharges.getAmount(), penaltyCharges.getAmount(), false);
|
feeCharges.getAmount(), penaltyCharges.getAmount(), false);
|
||||||
}
|
}
|
||||||
@@ -1588,7 +1620,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
private LoanTransaction getPreclosureTransaction(List<LoanTransaction> loanTransactions,
|
private LoanTransaction getPreclosureTransaction(List<LoanTransaction> loanTransactions,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency, final LoanApplicationTerms applicationTerms,
|
List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency, final LoanApplicationTerms applicationTerms,
|
||||||
MathContext mc) {
|
MathContext mc, final Set<LoanCharge> loanCharges) {
|
||||||
LoanTransaction precloseTransaction = null;
|
LoanTransaction precloseTransaction = null;
|
||||||
for (LoanTransaction loanTransaction : loanTransactions) {
|
for (LoanTransaction loanTransaction : loanTransactions) {
|
||||||
if (precloseTransaction == null || precloseTransaction.getTransactionDate().isBefore(loanTransaction.getTransactionDate())) {
|
if (precloseTransaction == null || precloseTransaction.getTransactionDate().isBefore(loanTransaction.getTransactionDate())) {
|
||||||
@@ -1597,7 +1629,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
if (precloseTransaction != null) {
|
if (precloseTransaction != null) {
|
||||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = calculatePrepaymentAmount(installments, currency,
|
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = calculatePrepaymentAmount(installments, currency,
|
||||||
precloseTransaction.getTransactionDate(), applicationTerms.getInterestChargedFromLocalDate(), applicationTerms, mc);
|
precloseTransaction.getTransactionDate(), applicationTerms.getInterestChargedFromLocalDate(), applicationTerms, mc,
|
||||||
|
loanCharges);
|
||||||
if (precloseTransaction.getAmount(currency).isLessThan(loanRepaymentScheduleInstallment.getTotalOutstanding(currency))) {
|
if (precloseTransaction.getAmount(currency).isLessThan(loanRepaymentScheduleInstallment.getTotalOutstanding(currency))) {
|
||||||
precloseTransaction = null;
|
precloseTransaction = null;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ public interface LoanScheduleGenerator {
|
|||||||
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom);
|
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom);
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
||||||
LocalDate onDate, LocalDate interestChargedFromLocalDate, LoanApplicationTerms loanApplicationTerms, MathContext mc);
|
LocalDate onDate, LocalDate interestChargedFromLocalDate, LoanApplicationTerms loanApplicationTerms, MathContext mc, Set<LoanCharge> charges);
|
||||||
|
|
||||||
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
||||||
|
|||||||
+3
-2
@@ -385,7 +385,8 @@ public class LoanScheduleAssembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
||||||
MonetaryCurrency currency, LocalDate onDate, LoanApplicationTerms loanApplicationTerms,final Long officeId) {
|
MonetaryCurrency currency, LocalDate onDate, LoanApplicationTerms loanApplicationTerms, final Long officeId,
|
||||||
|
final Set<LoanCharge> loanCharges) {
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
@@ -404,7 +405,7 @@ public class LoanScheduleAssembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return loanScheduleGenerator.calculatePrepaymentAmount(installments, currency, nextScheduleDate,
|
return loanScheduleGenerator.calculatePrepaymentAmount(installments, currency, nextScheduleDate,
|
||||||
loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms, mc);
|
loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms, mc, loanCharges);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateDisbursementDateIsOnNonWorkingDay(final LocalDate disbursementDate, final WorkingDays workingDays) {
|
private void validateDisbursementDateIsOnNonWorkingDay(final LocalDate disbursementDate, final WorkingDays workingDays) {
|
||||||
|
|||||||
+9
-1
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
|
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -127,6 +128,12 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
if (!loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || loan.isNpa() || !loan.status().isActive()
|
if (!loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || loan.isNpa() || !loan.status().isActive()
|
||||||
|| !loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) { return; }
|
|| !loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) { return; }
|
||||||
|
|
||||||
|
if (loan.loanProduct().isMultiDisburseLoan()) {
|
||||||
|
BigDecimal disbursedAmount = loan.getDisbursedAmount();
|
||||||
|
BigDecimal principalRepaid = loan.getLoanSummary().getTotalPrincipalRepaid();
|
||||||
|
BigDecimal principalWrittenOff = loan.getLoanSummary().getTotalPrincipalWrittenOff();
|
||||||
|
if (disbursedAmount.subtract(principalWrittenOff).subtract(principalRepaid).compareTo(BigDecimal.ZERO) != 1) { return; }
|
||||||
|
}
|
||||||
MonetaryCurrency currency = loan.getCurrency();
|
MonetaryCurrency currency = loan.getCurrency();
|
||||||
Money totalPrincipal = Money.zero(currency);
|
Money totalPrincipal = Money.zero(currency);
|
||||||
final List<LoanSchedulePeriodData> futureInstallments = new ArrayList<>();
|
final List<LoanSchedulePeriodData> futureInstallments = new ArrayList<>();
|
||||||
@@ -147,7 +154,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency,
|
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency,
|
||||||
calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
||||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(
|
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(
|
||||||
loan.fetchRepaymentScheduleInstallments(), currency, LocalDate.now(), loanApplicationTerms, loan.getOfficeId());
|
loan.fetchRepaymentScheduleInstallments(), currency, LocalDate.now(), loanApplicationTerms, loan.getOfficeId(),
|
||||||
|
loan.charges());
|
||||||
Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus(
|
Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus(
|
||||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||||
Money interestDue = Money.zero(currency);
|
Money interestDue = Money.zero(currency);
|
||||||
|
|||||||
+2
-2
@@ -156,7 +156,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
}
|
}
|
||||||
int daysToBeAccrued = Days.daysBetween(startDate, tilldate).getDays();
|
int daysToBeAccrued = Days.daysBetween(startDate, tilldate).getDays();
|
||||||
// if (daysToBeAccrued < 1) { return; }
|
// if (daysToBeAccrued < 1) { return; }
|
||||||
int daysInSchedule = Days.daysBetween(interestStartDate, tilldate).getDays();
|
// int daysInSchedule = Days.daysBetween(interestStartDate, tilldate).getDays();
|
||||||
/*
|
/*
|
||||||
* switch (accrualData.getRepaymentFrequency()) { case MONTHS: int
|
* switch (accrualData.getRepaymentFrequency()) { case MONTHS: int
|
||||||
* numberOfDaysInMonth =
|
* numberOfDaysInMonth =
|
||||||
@@ -175,7 +175,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
BigDecimal interestportion = null;
|
BigDecimal interestportion = null;
|
||||||
BigDecimal feeportion = accrualData.getDueDateFeeIncome();
|
BigDecimal feeportion = accrualData.getDueDateFeeIncome();
|
||||||
BigDecimal penaltyportion = accrualData.getDueDatePenaltyIncome();
|
BigDecimal penaltyportion = accrualData.getDueDatePenaltyIncome();
|
||||||
if (daysInSchedule >= totalNumberOfDays) {
|
if (daysToBeAccrued >= totalNumberOfDays) {
|
||||||
interestportion = accrualData.getInterestIncome();
|
interestportion = accrualData.getInterestIncome();
|
||||||
} else {
|
} else {
|
||||||
double iterest = interestPerDay * daysToBeAccrued;
|
double iterest = interestPerDay * daysToBeAccrued;
|
||||||
|
|||||||
+27
-13
@@ -83,6 +83,7 @@ import org.mifosplatform.portfolio.collectionsheet.command.SingleRepaymentComman
|
|||||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.mifosplatform.portfolio.group.domain.Group;
|
import org.mifosplatform.portfolio.group.domain.Group;
|
||||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||||
@@ -280,7 +281,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
ChangedTransactionDetail changedTransactionDetail = null;
|
ChangedTransactionDetail changedTransactionDetail = null;
|
||||||
if (canDisburse) {
|
if (canDisburse) {
|
||||||
Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate);
|
Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate);
|
||||||
boolean recalcualteSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
|
boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
|
||||||
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||||
if (isAccountTransfer) {
|
if (isAccountTransfer) {
|
||||||
disburseLoanToSavings(loan, command, disburseAmount, paymentDetail);
|
disburseLoanToSavings(loan, command, disburseAmount, paymentDetail);
|
||||||
@@ -301,9 +302,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regenerateScheduleOnDisbursement(command, loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||||
|
workingDays, recalculateSchedule);
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
|
||||||
|
loan, null);
|
||||||
|
}
|
||||||
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
|
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||||
allowTransactionsOnNonWorkingDay, recalcualteSchedule, restCalendarInstance);
|
allowTransactionsOnNonWorkingDay, recalculateSchedule, restCalendarInstance);
|
||||||
}
|
}
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
@@ -322,10 +329,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
|
||||||
createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
|
||||||
workingDays);
|
|
||||||
}
|
|
||||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -476,7 +480,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
if (canDisburse) {
|
if (canDisburse) {
|
||||||
Money amountBeforeAdjust = loan.getPrincpal();
|
Money amountBeforeAdjust = loan.getPrincpal();
|
||||||
Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate);
|
Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate);
|
||||||
boolean recalcualteSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
|
boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
|
||||||
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||||
if (isAccountTransfer) {
|
if (isAccountTransfer) {
|
||||||
disburseLoanToSavings(loan, command, disburseAmount, paymentDetail);
|
disburseLoanToSavings(loan, command, disburseAmount, paymentDetail);
|
||||||
@@ -496,10 +500,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
}
|
}
|
||||||
|
regenerateScheduleOnDisbursement(command, loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays,
|
||||||
|
workingDays, recalculateSchedule);
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
|
||||||
|
loan, null);
|
||||||
|
}
|
||||||
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
|
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
|
||||||
firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||||
allowTransactionsOnNonWorkingDay, recalcualteSchedule, restCalendarInstance);
|
allowTransactionsOnNonWorkingDay, recalculateSchedule, restCalendarInstance);
|
||||||
}
|
}
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
|
|
||||||
@@ -518,10 +527,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
|
||||||
createLoanScheduleArchive(loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays);
|
|
||||||
}
|
|
||||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
}
|
}
|
||||||
final Set<LoanCharge> loanCharges = loan.charges();
|
final Set<LoanCharge> loanCharges = loan.charges();
|
||||||
@@ -2315,6 +2320,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final ApplicationCurrency applicationCurrency,
|
||||||
|
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||||
|
final WorkingDays workingDays, final boolean recalculateSchedule) {
|
||||||
|
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
|
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
||||||
|
loan.regenerateScheduleOnDisbursement(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate,
|
||||||
|
isHolidayEnabled, holidays, workingDays, recalculateSchedule, actualDisbursementDate, emiAmount);
|
||||||
|
}
|
||||||
|
|
||||||
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {
|
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {
|
||||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : model.getPeriods()) {
|
for (final LoanScheduleModelPeriod scheduledLoanInstallment : model.getPeriods()) {
|
||||||
|
|||||||
+4
-3
@@ -78,12 +78,13 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable
|
|||||||
RecalculationFrequencyType frequencyType = RecalculationFrequencyType.fromInt(recurrenceFrequency);
|
RecalculationFrequencyType frequencyType = RecalculationFrequencyType.fromInt(recurrenceFrequency);
|
||||||
Date recurrenceOnDate = null;
|
Date recurrenceOnDate = null;
|
||||||
if (recurrenceOnLocalDate != null) {
|
if (recurrenceOnLocalDate != null) {
|
||||||
if (frequencyType.isSameAsRepayment()) {
|
if (!frequencyType.isSameAsRepayment()) {
|
||||||
recurrenceInterval = 0;
|
|
||||||
} else {
|
|
||||||
recurrenceOnDate = recurrenceOnLocalDate.toDate();
|
recurrenceOnDate = recurrenceOnLocalDate.toDate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (frequencyType.isSameAsRepayment()) {
|
||||||
|
recurrenceInterval = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return new LoanProductInterestRecalculationDetails(interestRecalculationCompoundingMethod, loanRescheduleStrategyMethod,
|
return new LoanProductInterestRecalculationDetails(interestRecalculationCompoundingMethod, loanRescheduleStrategyMethod,
|
||||||
recurrenceFrequency, recurrenceInterval, recurrenceOnDate);
|
recurrenceFrequency, recurrenceInterval, recurrenceOnDate);
|
||||||
|
|||||||
Reference in New Issue
Block a user