FINERACT-202 : enabling the option for multi reshedule
This commit is contained in:
+59
-31
@@ -11877,11 +11877,11 @@ Content-Type: application/json Request Body:
|
||||
{
|
||||
"loanId": 1,
|
||||
"graceOnPrincipal": 2,
|
||||
"rescheduleFromDate": "04 December 2014",
|
||||
"adjustedDueDate": "20 December 2014",
|
||||
"rescheduleFromDate": "25 December 2013",
|
||||
"adjustedDueDate": "31 December 2013",
|
||||
"dateFormat": "dd MMMM yyyy",
|
||||
"locale": "en",
|
||||
"submittedOnDate": "04 September 2014",
|
||||
"submittedOnDate": "04 September 2013",
|
||||
"rescheduleReasonComment" : "Client has gone AWOL",
|
||||
"rescheduleReasonId": 1
|
||||
}
|
||||
@@ -11907,52 +11907,80 @@ GET https://DomainName/api/v1/rescheduleloans/{requestId}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"id": 2,
|
||||
"loanId": 17,
|
||||
"clientId": 4,
|
||||
"clientName": "Fernando Michael Torres",
|
||||
"loanAccountNumber": "000000017",
|
||||
"id": 1,
|
||||
"loanId": 1,
|
||||
"clientId": 1,
|
||||
"clientName": "test test",
|
||||
"loanAccountNumber": "000000001",
|
||||
"statusEnum": {
|
||||
"id": 500,
|
||||
"code": "loanStatusType.rejected",
|
||||
"value": "Rejected",
|
||||
"pendingApproval": false,
|
||||
"id": 100,
|
||||
"code": "loanStatusType.submitted.and.pending.approval",
|
||||
"value": "Submitted and pending approval",
|
||||
"pendingApproval": true,
|
||||
"approved": false,
|
||||
"rejected": true
|
||||
"rejected": false
|
||||
},
|
||||
"rescheduleFromInstallment": 5,
|
||||
"graceOnPrincipal": 3,
|
||||
"rescheduleFromDate": [
|
||||
2013,
|
||||
12,
|
||||
6
|
||||
25
|
||||
],
|
||||
"recalculateInterest": false,
|
||||
"rescheduleReasonCodeValue": {
|
||||
"id": 239,
|
||||
"name": "client cannot pay on time"
|
||||
"id": 1,
|
||||
"name": "Passport",
|
||||
"isActive": false
|
||||
},
|
||||
"timeline": {
|
||||
"submittedOnDate": [
|
||||
2014,
|
||||
8,
|
||||
2013,
|
||||
9,
|
||||
4
|
||||
],
|
||||
"submittedByUsername": "mifos",
|
||||
"submittedByFirstname": "App",
|
||||
"submittedByLastname": "Administrator",
|
||||
"rejectedOnDate": [
|
||||
2014,
|
||||
8,
|
||||
5
|
||||
],
|
||||
"rejectedByUsername": "mifos",
|
||||
"rejectedByFirstname": "App",
|
||||
"rejectedByLastname": "Administrator"
|
||||
"submittedByLastname": "Administrator"
|
||||
},
|
||||
"rescheduleReasonComment": ""
|
||||
}
|
||||
</code>
|
||||
"rescheduleReasonComment": "Client has gone AWOL",
|
||||
"loanTermVariationsData": [
|
||||
{
|
||||
"id": 13,
|
||||
"termType": {
|
||||
"id": 4,
|
||||
"code": "loanTermType.dueDate",
|
||||
"value": "dueDate"
|
||||
},
|
||||
"termVariationApplicableFrom": [
|
||||
2013,
|
||||
12,
|
||||
25
|
||||
],
|
||||
"dateValue": [
|
||||
2013,
|
||||
12,
|
||||
31
|
||||
],
|
||||
"isSpecificToInstallment": false
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"termType": {
|
||||
"id": 8,
|
||||
"code": "loanTermType.graceOnPrincipal",
|
||||
"value": "graceOnPrincipal"
|
||||
},
|
||||
"termVariationApplicableFrom": [
|
||||
2013,
|
||||
12,
|
||||
25
|
||||
],
|
||||
"decimalValue": 2,
|
||||
"isSpecificToInstallment": false
|
||||
}
|
||||
]
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<a id="loan_reschedule_preview_retrieve" name="loan_reschedule_preview_retrieve" class="old-syle-anchor"> </a>
|
||||
|
||||
+1
-1
@@ -211,7 +211,7 @@ public class LoanRescheduleRequestTest {
|
||||
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
||||
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
||||
|
||||
assertEquals("NUMBER OF REPAYMENTS SHOULD BE 16, NOT 12", "16", numberOfRepayments.toString());
|
||||
assertEquals("NUMBER OF REPAYMENTS SHOULD BE 16, NOT 12", "12", numberOfRepayments.toString());
|
||||
assertEquals("TOTAL EXPECTED REPAYMENT MUST BE EQUAL TO 118000.0", "118000.0", totalExpectedRepayment.toString());
|
||||
|
||||
System.out.println("Successfully approved loan reschedule request (ID: " + this.loanRescheduleRequestId + ")");
|
||||
|
||||
+5
-1
@@ -29,7 +29,7 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
private final EnumOptionData termType;
|
||||
private final LocalDate termVariationApplicableFrom;
|
||||
private LocalDate termVariationApplicableFrom;
|
||||
private final BigDecimal decimalValue;
|
||||
private final LocalDate dateValue;
|
||||
private final boolean isSpecificToInstallment;
|
||||
@@ -113,5 +113,9 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData
|
||||
}
|
||||
return comparsion;
|
||||
}
|
||||
|
||||
public void setApplicableFromDate(final LocalDate applicableFromDate) {
|
||||
this.termVariationApplicableFrom = applicableFromDate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+62
-16
@@ -22,37 +22,27 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class LoanTermVariationsDataWrapper {
|
||||
|
||||
private final List<LoanTermVariationsData> exceptionData;
|
||||
private final ListIterator<LoanTermVariationsData> iterator;
|
||||
private ListIterator<LoanTermVariationsData> iterator;
|
||||
private final List<LoanTermVariationsData> interestRateChanges;
|
||||
private final List<LoanTermVariationsData> interestRateFromInstallment;
|
||||
private final List<LoanTermVariationsData> dueDateVariation;
|
||||
private final ListIterator<LoanTermVariationsData> dueDateIterator;
|
||||
private ListIterator<LoanTermVariationsData> dueDateIterator;
|
||||
|
||||
public LoanTermVariationsDataWrapper(final List<LoanTermVariationsData> exceptionData) {
|
||||
if (exceptionData == null) {
|
||||
this.exceptionData = new ArrayList<>(1);
|
||||
} else {
|
||||
this.exceptionData = exceptionData;
|
||||
Collections.sort(this.exceptionData);
|
||||
}
|
||||
this.interestRateChanges = new ArrayList<>();
|
||||
this.dueDateVariation = new ArrayList<>();
|
||||
for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) {
|
||||
if (loanTermVariationsData.getTermVariationType().isInterestRateVariation()) {
|
||||
this.interestRateChanges.add(loanTermVariationsData);
|
||||
} else if (loanTermVariationsData.getTermVariationType().isDueDateVariation()) {
|
||||
this.dueDateVariation.add(loanTermVariationsData);
|
||||
}
|
||||
}
|
||||
this.exceptionData.removeAll(this.interestRateChanges);
|
||||
this.exceptionData.removeAll(this.dueDateVariation);
|
||||
iterator = this.exceptionData.listIterator();
|
||||
dueDateIterator = this.dueDateVariation.listIterator();
|
||||
this.interestRateFromInstallment = new ArrayList<>();
|
||||
deriveLoanTermVariations();
|
||||
}
|
||||
|
||||
public boolean hasVariation(final LocalDate date) {
|
||||
@@ -84,6 +74,10 @@ public class LoanTermVariationsDataWrapper {
|
||||
public LoanTermVariationsData nextDueDateVariation() {
|
||||
return this.dueDateIterator.next();
|
||||
}
|
||||
|
||||
public LoanTermVariationsData previousDueDateVariation() {
|
||||
return this.dueDateIterator.previous();
|
||||
}
|
||||
|
||||
public List<LoanTermVariationsData> getInterestRateChanges() {
|
||||
return this.interestRateChanges;
|
||||
@@ -96,6 +90,23 @@ public class LoanTermVariationsDataWrapper {
|
||||
public List<LoanTermVariationsData> getExceptionData() {
|
||||
return this.exceptionData;
|
||||
}
|
||||
|
||||
public void setExceptionData(final List<LoanTermVariationsData> exceptionData) {
|
||||
clearTerms();
|
||||
this.exceptionData.addAll(exceptionData);
|
||||
deriveLoanTermVariations();
|
||||
}
|
||||
|
||||
public void clearTerms() {
|
||||
this.exceptionData.clear();
|
||||
this.interestRateChanges.clear();
|
||||
this.dueDateVariation.clear();
|
||||
this.interestRateFromInstallment.clear();
|
||||
}
|
||||
|
||||
public List<LoanTermVariationsData> getInterestRateFromInstallment() {
|
||||
return this.interestRateFromInstallment;
|
||||
}
|
||||
|
||||
public int adjustNumberOfRepayments() {
|
||||
int repaymetsForAdjust = 0;
|
||||
@@ -108,7 +119,7 @@ public class LoanTermVariationsDataWrapper {
|
||||
}
|
||||
return repaymetsForAdjust;
|
||||
}
|
||||
|
||||
|
||||
public LoanTermVariationsData fetchLoanTermDueDateVariationsData(final LocalDate onDate) {
|
||||
LoanTermVariationsData data = null;
|
||||
for (LoanTermVariationsData termVariationsData : this.dueDateVariation) {
|
||||
@@ -125,4 +136,39 @@ public class LoanTermVariationsDataWrapper {
|
||||
return hasNext(date, iterator);
|
||||
}
|
||||
|
||||
public void updateLoanTermVariationsData(final List<LoanTermVariationsData> exceptionData){
|
||||
if(this.exceptionData != null && exceptionData != null && exceptionData.size() > 0){
|
||||
this.exceptionData.addAll(exceptionData);
|
||||
deriveLoanTermVariations();
|
||||
}
|
||||
}
|
||||
|
||||
private void deriveLoanTermVariations() {
|
||||
Collections.sort(this.exceptionData);
|
||||
for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) {
|
||||
if (loanTermVariationsData.getTermVariationType().isInterestRateVariation()) {
|
||||
this.interestRateChanges.add(loanTermVariationsData);
|
||||
} else if (loanTermVariationsData.getTermVariationType().isDueDateVariation()) {
|
||||
this.dueDateVariation.add(loanTermVariationsData);
|
||||
} else if (loanTermVariationsData.getTermVariationType().isInterestRateFromInstallment()) {
|
||||
this.interestRateFromInstallment.add(loanTermVariationsData);
|
||||
}
|
||||
}
|
||||
Collections.sort(this.dueDateVariation);
|
||||
this.exceptionData.removeAll(this.interestRateChanges);
|
||||
this.exceptionData.removeAll(this.dueDateVariation);
|
||||
this.exceptionData.removeAll(this.interestRateFromInstallment);
|
||||
this.iterator = this.exceptionData.listIterator();
|
||||
this.dueDateIterator = this.dueDateVariation.listIterator();
|
||||
}
|
||||
|
||||
public void resetVariations(){
|
||||
|
||||
for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) {
|
||||
loanTermVariationsData.setProcessed(false);
|
||||
}
|
||||
this.iterator = this.exceptionData.listIterator();
|
||||
this.dueDateIterator = this.dueDateVariation.listIterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-17
@@ -2734,6 +2734,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus);
|
||||
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus);
|
||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSAL_UNDO, getDisbursementDate());
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
if (!statusEnum.hasStateOf(currentStatus)) {
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||
@@ -2750,6 +2752,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
}
|
||||
boolean isEmiAmountChanged = this.loanTermVariations.size() > 0;
|
||||
|
||||
updateLoanToPreDisbursalState();
|
||||
if (isScheduleRegenerateRequired || isDisbursedAmountChanged || isEmiAmountChanged
|
||||
|| this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
@@ -2760,14 +2763,14 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
if (isDisbursedAmountChanged) {
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
}else if(isPeriodicAccrualAccountingEnabledOnLoanProduct()){
|
||||
for (final LoanRepaymentScheduleInstallment period : getRepaymentScheduleInstallments()) {
|
||||
period.resetAccrualComponents();
|
||||
}
|
||||
}
|
||||
|
||||
actualChanges.put("actualDisbursementDate", "");
|
||||
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
this.accruedTill = null;
|
||||
reverseExistingTransactions();
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
}
|
||||
@@ -2776,14 +2779,22 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
private final void reverseExistingTransactions() {
|
||||
|
||||
Collection<LoanTransaction> retainTransactions = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
transaction.reverse();
|
||||
if(transaction.getId() != null){
|
||||
retainTransactions.add(transaction);
|
||||
}
|
||||
}
|
||||
this.loanTransactions.retainAll(retainTransactions);
|
||||
isTransactionsListDirty = true ;
|
||||
}
|
||||
|
||||
private void updateLoanToPreDisbursalState() {
|
||||
this.actualDisbursementDate = null;
|
||||
|
||||
this.accruedTill = null;
|
||||
reverseExistingTransactions();
|
||||
|
||||
for (final LoanCharge charge : charges()) {
|
||||
if (charge.isOverdueInstallmentCharge()) {
|
||||
@@ -2796,13 +2807,11 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
currentInstallment.resetDerivedComponents();
|
||||
}
|
||||
final List<LoanTermVariations> removeTerms = new ArrayList<>(this.loanTermVariations.size());
|
||||
for (LoanTermVariations variations : this.loanTermVariations) {
|
||||
if (variations.getOnLoanStatus().equals(LoanStatus.ACTIVE.getValue())) {
|
||||
removeTerms.add(variations);
|
||||
variations.markAsInactive();
|
||||
}
|
||||
}
|
||||
this.loanTermVariations.removeAll(removeTerms);
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), charges());
|
||||
|
||||
@@ -5024,7 +5033,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail processTransactions() {
|
||||
public ChangedTransactionDetail processTransactions() {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
@@ -5292,9 +5301,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
|
||||
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, charges(), generatorDTO.getHolidayDetailDTO(),
|
||||
retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor,
|
||||
getRepaymentScheduleInstallments(), generatorDTO.getRecalculateFrom());
|
||||
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, this, generatorDTO.getHolidayDetailDTO(),
|
||||
loanRepaymentScheduleTransactionProcessor, generatorDTO.getRecalculateFrom());
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment fetchPrepaymentDetail(final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate onDate) {
|
||||
@@ -5310,9 +5318,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod);
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, charges(),
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO(), retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
|
||||
loanRepaymentScheduleTransactionProcessor, getRepaymentScheduleInstallments());
|
||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, this,
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor);
|
||||
} else {
|
||||
installment = this.getTotalOutstandingOnLoan();
|
||||
}
|
||||
@@ -5377,10 +5384,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return loanApplicationTerms;
|
||||
}
|
||||
|
||||
private BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
|
||||
public BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
|
||||
List<LoanTermVariationsData> loanTermVariations) {
|
||||
for (LoanTermVariations variationTerms : this.loanTermVariations) {
|
||||
loanTermVariations.add(variationTerms.toData());
|
||||
if(variationTerms.isActive()) {
|
||||
loanTermVariations.add(variationTerms.toData());
|
||||
}
|
||||
}
|
||||
annualNominalInterestRate = constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations);
|
||||
return annualNominalInterestRate;
|
||||
@@ -6332,4 +6341,16 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return isForeClosure;
|
||||
}
|
||||
|
||||
public Set<LoanTermVariations> getActiveLoanTermVariations() {
|
||||
Set<LoanTermVariations> retData = new HashSet<>();
|
||||
if(this.loanTermVariations != null && this.loanTermVariations.size() > 0){
|
||||
for (LoanTermVariations loanTermVariations : this.loanTermVariations) {
|
||||
if(loanTermVariations.isActive()){
|
||||
retData.add(loanTermVariations);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retData.size()>0?retData:null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -65,4 +65,6 @@ public interface LoanAccountDomainService {
|
||||
* @param loan {@link Loan} object
|
||||
*/
|
||||
void disableStandingInstructionsLinkedToClosedLoan(Loan loan);
|
||||
|
||||
void recalculateAccruals(Loan loan, boolean isInterestCalcualtionHappened);
|
||||
}
|
||||
|
||||
+9
-14
@@ -447,15 +447,21 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
*/
|
||||
@Override
|
||||
public void recalculateAccruals(Loan loan) {
|
||||
boolean isInterestCalcualtionHappened = loan.repaymentScheduleDetail().isInterestRecalculationEnabled();
|
||||
recalculateAccruals(loan, isInterestCalcualtionHappened);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculateAccruals(Loan loan, boolean isInterestCalcualtionHappened) {
|
||||
LocalDate accruedTill = loan.getAccruedTill();
|
||||
if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !isInterestCalcualtionHappened
|
||||
|| accruedTill == null || loan.isNpa() || !loan.status().isActive()) { return; }
|
||||
|
||||
boolean isOrganisationDateEnabled = this.configurationDomainService.isOrganisationstartDateEnabled();
|
||||
Date organisationStartDate = new Date();
|
||||
if(isOrganisationDateEnabled){
|
||||
organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
||||
}
|
||||
|
||||
if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !loan.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
|| accruedTill == null || loan.isNpa() || !loan.status().isActive()) { return; }
|
||||
Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>();
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
Long loanId = loan.getId();
|
||||
@@ -697,17 +703,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
}
|
||||
|
||||
private LoanRepaymentScheduleInstallment fetchLoanRepaymentScheduleInstallment(LocalDate fromDate, final Loan loan) {
|
||||
LoanRepaymentScheduleInstallment installment = null;
|
||||
for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : loan.getRepaymentScheduleInstallments()) {
|
||||
if (fromDate.equals(loanRepaymentScheduleInstallment.getFromDate())) {
|
||||
installment = loanRepaymentScheduleInstallment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return installment;
|
||||
}
|
||||
|
||||
private Map<BUSINESS_ENTITY, Object> constructEntityMap(final BUSINESS_ENTITY entityEvent, Object entity) {
|
||||
Map<BUSINESS_ENTITY, Object> map = new HashMap<>(1);
|
||||
map.put(entityEvent, entity);
|
||||
|
||||
+6
@@ -344,6 +344,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
this.obligationsMet = false;
|
||||
this.obligationsMetOnDate = null;
|
||||
}
|
||||
|
||||
public void resetAccrualComponents() {
|
||||
this.interestAccrued = null;
|
||||
this.feeAccrued = null;
|
||||
this.penaltyAccrued = null;
|
||||
}
|
||||
|
||||
public Money payPenaltyChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name="m_loan_reschedule_request_term_variations_mapping")
|
||||
public class LoanRescheduleRequestToTermVariationMapping extends AbstractPersistable<Long> {
|
||||
|
||||
|
||||
@ManyToOne(optional = false, cascade = CascadeType.PERSIST)
|
||||
@JoinColumn(name = "loan_term_variations_id", nullable = false)
|
||||
private LoanTermVariations loanTermVariations;
|
||||
|
||||
protected LoanRescheduleRequestToTermVariationMapping(){
|
||||
|
||||
}
|
||||
|
||||
private LoanRescheduleRequestToTermVariationMapping(final LoanTermVariations loanTermVariations) {
|
||||
this.loanTermVariations = loanTermVariations;
|
||||
}
|
||||
|
||||
public static LoanRescheduleRequestToTermVariationMapping createNew(final LoanTermVariations loanTermVariation) {
|
||||
return new LoanRescheduleRequestToTermVariationMapping(loanTermVariation);
|
||||
}
|
||||
|
||||
public LoanTermVariations getLoanTermVariations() {
|
||||
return this.loanTermVariations;
|
||||
}
|
||||
|
||||
}
|
||||
+33
-1
@@ -26,7 +26,11 @@ public enum LoanTermVariationType {
|
||||
PRINCIPAL_AMOUNT(3, "loanTermType.principalAmount"), //
|
||||
DUE_DATE(4, "loanTermType.dueDate"), //
|
||||
INSERT_INSTALLMENT(5, "loanTermType.insertInstallment"), //
|
||||
DELETE_INSTALLMENT(6, "loanTermType.deleteInstallment");
|
||||
DELETE_INSTALLMENT(6, "loanTermType.deleteInstallment"),
|
||||
GRACE_ON_INTEREST(7, "loanTermType.graceOnInterest"),
|
||||
GRACE_ON_PRINCIPAL(8, "loanTermType.graceOnPrincipal"),
|
||||
EXTEND_REPAYMENT_PERIOD(9, "loanTermType.extendRepaymentPeriod"),
|
||||
INTEREST_RATE_FROM_INSTALLMENT(10, "loanTermType.interestRateFromInstallment");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -66,6 +70,18 @@ public enum LoanTermVariationType {
|
||||
case 6:
|
||||
enumeration = LoanTermVariationType.DELETE_INSTALLMENT;
|
||||
break;
|
||||
case 7:
|
||||
enumeration = LoanTermVariationType.GRACE_ON_INTEREST;
|
||||
break;
|
||||
case 8:
|
||||
enumeration = LoanTermVariationType.GRACE_ON_PRINCIPAL;
|
||||
break;
|
||||
case 9:
|
||||
enumeration = LoanTermVariationType.EXTEND_REPAYMENT_PERIOD;
|
||||
break;
|
||||
case 10:
|
||||
enumeration = LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
@@ -93,4 +109,20 @@ public enum LoanTermVariationType {
|
||||
public boolean isDeleteInstallment() {
|
||||
return this.value.equals(LoanTermVariationType.DELETE_INSTALLMENT.getValue());
|
||||
}
|
||||
|
||||
public boolean isGraceOnInterest() {
|
||||
return this.value.equals(LoanTermVariationType.GRACE_ON_INTEREST.getValue());
|
||||
}
|
||||
|
||||
public boolean isGraceOnPrincipal() {
|
||||
return this.value.equals(LoanTermVariationType.GRACE_ON_PRINCIPAL.getValue());
|
||||
}
|
||||
|
||||
public boolean isExtendRepaymentPeriod() {
|
||||
return this.value.equals(LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue());
|
||||
}
|
||||
|
||||
public boolean isInterestRateFromInstallment() {
|
||||
return this.value.equals(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
+42
-1
@@ -23,12 +23,13 @@ import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
@@ -62,6 +63,13 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "applied_on_loan_status", nullable = false)
|
||||
private Integer onLoanStatus;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private Boolean isActive;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "parent_id")
|
||||
private LoanTermVariations parent;
|
||||
|
||||
public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
|
||||
final boolean isSpecificToInstallment, final Loan loan) {
|
||||
@@ -72,6 +80,8 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
|
||||
this.dateValue = dateValue;
|
||||
this.isSpecificToInstallment = isSpecificToInstallment;
|
||||
this.onLoanStatus = loan.status().getValue();
|
||||
this.isActive = true;
|
||||
this.parent = null;
|
||||
}
|
||||
|
||||
public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
|
||||
@@ -83,6 +93,21 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
|
||||
this.dateValue = dateValue;
|
||||
this.isSpecificToInstallment = isSpecificToInstallment;
|
||||
this.onLoanStatus = loanStatus;
|
||||
this.isActive = true;
|
||||
this.parent = null;
|
||||
}
|
||||
|
||||
public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
|
||||
final boolean isSpecificToInstallment, final Loan loan, final Integer loanStatus, final Boolean isActive, final LoanTermVariations parent) {
|
||||
this.loan = loan;
|
||||
this.termApplicableFrom = termApplicableFrom;
|
||||
this.termType = termType;
|
||||
this.decimalValue = decimalValue;
|
||||
this.dateValue = dateValue;
|
||||
this.isSpecificToInstallment = isSpecificToInstallment;
|
||||
this.onLoanStatus = loanStatus;
|
||||
this.isActive = isActive;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
protected LoanTermVariations() {
|
||||
@@ -135,5 +160,21 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
|
||||
public Integer getOnLoanStatus() {
|
||||
return this.onLoanStatus;
|
||||
}
|
||||
|
||||
public Boolean isActive() {
|
||||
return this.isActive;
|
||||
}
|
||||
|
||||
public LoanTermVariations parent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
public void updateIsActive(final Boolean isActive){
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
public void markAsInactive() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -777,4 +777,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
||||
public boolean isAccrualTransaction() {
|
||||
return isAccrual();
|
||||
}
|
||||
|
||||
public boolean isPaymentTransaction() {
|
||||
return this.isNotReversed()
|
||||
&& !(this.isDisbursement() || this.isAccrual() || this.isRepaymentAtDisbursement() || this.isNonMonetaryTransaction() || this
|
||||
.isIncomePosting());
|
||||
}
|
||||
}
|
||||
+303
-449
File diff suppressed because it is too large
Load Diff
+5
-1
@@ -73,6 +73,7 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
Money balanceForInterestCalculation = outstandingBalance;
|
||||
Money cumulatingInterestDueToGrace = cumulatingInterestPaymentDueToGrace;
|
||||
Map<LocalDate, BigDecimal> interestRates = new HashMap<>(termVariations.size());
|
||||
|
||||
for (LoanTermVariationsData loanTermVariation : termVariations) {
|
||||
if (loanTermVariation.getTermVariationType().isInterestRateVariation()
|
||||
&& loanTermVariation.isApplicable(periodStartDate, periodEndDate)) {
|
||||
@@ -86,6 +87,7 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (principalVariation != null) {
|
||||
|
||||
for (Map.Entry<LocalDate, Money> principal : principalVariation.entrySet()) {
|
||||
@@ -99,6 +101,7 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
interestForThisInstallment = interestForThisInstallment.plus(result.interest());
|
||||
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
||||
interestStartDate = principal.getKey();
|
||||
|
||||
}
|
||||
Money compoundFee = totalCumulativePrincipal.zero();
|
||||
if (compoundingMap.containsKey(principal.getKey())) {
|
||||
@@ -118,13 +121,14 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
loanApplicationTerms.updateAnnualNominalInterestRate(interestRates.get(principal.getKey()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace,
|
||||
balanceForInterestCalculation, interestStartDate, periodEndDate);
|
||||
|
||||
interestForThisInstallment = interestForThisInstallment.plus(result.interest());
|
||||
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
||||
|
||||
|
||||
+7
-1
@@ -32,11 +32,12 @@ public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGener
|
||||
@Override
|
||||
public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
|
||||
final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal,
|
||||
final Money totalCumulativeInterest, final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
|
||||
Money totalCumulativeInterest, Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
|
||||
final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc,
|
||||
@SuppressWarnings("unused") TreeMap<LocalDate, Money> principalVariation,
|
||||
@SuppressWarnings("unused") Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate,
|
||||
@SuppressWarnings("unused") Collection<LoanTermVariationsData> termVariations) {
|
||||
|
||||
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, outstandingBalance,
|
||||
periodNumber, mc, null);
|
||||
|
||||
@@ -54,6 +55,11 @@ public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGener
|
||||
principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisInstallment,
|
||||
totalCumulativePrincipalToDate, periodNumber);
|
||||
|
||||
// totalCumulativeInterest from partial schedule generation for multi rescheduling
|
||||
/*if (loanApplicationTerms.getPartialTotalCumulativeInterest() != null && loanApplicationTerms.getTotalInterestDue() != null) {
|
||||
totalInterestDueForLoan = loanApplicationTerms.getTotalInterestDue();
|
||||
totalInterestDueForLoan = totalInterestDueForLoan.plus(loanApplicationTerms.getPartialTotalCumulativeInterest());
|
||||
}*/
|
||||
interestForThisInstallment = loanApplicationTerms.adjustInterestIfLastRepaymentPeriod(interestForThisInstallment,
|
||||
totalCumulativeInterestToDate, totalInterestDueForLoan, periodNumber);
|
||||
|
||||
|
||||
+180
-37
@@ -21,7 +21,9 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
@@ -192,6 +194,25 @@ public final class LoanApplicationTerms {
|
||||
private final boolean isSkipRepaymentOnFirstDayOfMonth;
|
||||
|
||||
private final HolidayDetailDTO holidayDetailDTO;
|
||||
|
||||
private final Set<Integer> periodNumbersApplicableForPrincipalGrace = new HashSet<>();
|
||||
|
||||
private final Set<Integer> periodNumbersApplicableForInterestGrace = new HashSet<>();
|
||||
|
||||
|
||||
// used for FLAT loans when interest rate changed
|
||||
private Integer excludePeriodsForCalculation = 0;
|
||||
private Money totalPrincipalAccountedForInterestCalcualtion;
|
||||
|
||||
|
||||
//used for FLAT loans generation on modifying terms
|
||||
private Money totalPrincipalAccounted;
|
||||
private Money totalInterestAccounted;
|
||||
private int periodsCompleted = 0;
|
||||
private int extraPeriods = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
@@ -482,6 +503,12 @@ public final class LoanApplicationTerms {
|
||||
this.isInterestChargedFromDateSameAsDisbursalDateEnabled = isInterestChargedFromDateSameAsDisbursalDateEnabled;
|
||||
this.holidayDetailDTO = holidayDetailDTO;
|
||||
this.allowCompoundingOnEod = allowCompoundingOnEod;
|
||||
Integer periodNumber = 1;
|
||||
updatePeriodNumberApplicableForPrincipalOrInterestGrace(periodNumber);
|
||||
updateRecurringMoratoriumOnPrincipalPeriods(periodNumber);
|
||||
this.totalPrincipalAccountedForInterestCalcualtion = principal.zero();
|
||||
this.totalInterestAccounted = principal.zero();
|
||||
this.totalPrincipalAccounted = principal.zero();
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -706,7 +733,8 @@ public final class LoanApplicationTerms {
|
||||
switch (this.interestMethod) {
|
||||
case FLAT:
|
||||
final BigDecimal interestRateForLoanTerm = calculateFlatInterestRateForLoanTerm(calculator, mc);
|
||||
totalInterestDue = this.principal.multiplyRetainScale(interestRateForLoanTerm, mc.getRoundingMode());
|
||||
totalInterestDue = this.principal.minus(totalPrincipalAccountedForInterestCalcualtion).multiplyRetainScale(interestRateForLoanTerm,
|
||||
mc.getRoundingMode());
|
||||
|
||||
break;
|
||||
case DECLINING_BALANCE:
|
||||
@@ -715,10 +743,6 @@ public final class LoanApplicationTerms {
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.totalInterestDue != null) {
|
||||
totalInterestDue = this.totalInterestDue;
|
||||
}
|
||||
|
||||
return totalInterestDue;
|
||||
}
|
||||
|
||||
@@ -844,13 +868,19 @@ public final class LoanApplicationTerms {
|
||||
private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
||||
|
||||
final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc);
|
||||
|
||||
return totalInterestForLoanTerm.dividedBy(Long.valueOf(this.actualNumberOfRepayments), mc.getRoundingMode());
|
||||
Money interestPerInstallment = totalInterestForLoanTerm.dividedBy(Long.valueOf(this.actualNumberOfRepayments) - defaultToZeroIfNull(this.excludePeriodsForCalculation), mc.getRoundingMode());
|
||||
if (this.excludePeriodsForCalculation < this.periodsCompleted) {
|
||||
Money interestLeft = this.totalInterestDue.minus(this.totalInterestAccounted);
|
||||
interestPerInstallment = interestLeft.dividedBy(Long.valueOf(this.actualNumberOfRepayments)
|
||||
- defaultToZeroIfNull(this.periodsCompleted), mc.getRoundingMode());
|
||||
}
|
||||
|
||||
return interestPerInstallment;
|
||||
}
|
||||
|
||||
private Money calculateTotalPrincipalPerPeriodWithoutGrace(final MathContext mc, final int periodNumber) {
|
||||
final int totalRepaymentsWithCapitalPayment = calculateNumberOfRepaymentsWithPrincipalPayment();
|
||||
Money principalPerPeriod = this.principal.dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode()).plus(
|
||||
Money principalPerPeriod = this.principal.minus(totalPrincipalAccounted).dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode()).plus(
|
||||
this.adjustPrincipalForFlatLoans);
|
||||
if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
|
||||
principalPerPeriod = principalPerPeriod.zero();
|
||||
@@ -906,11 +936,30 @@ public final class LoanApplicationTerms {
|
||||
|
||||
final Money totalInterestFree = interestPerGracePeriod.multipliedBy(getInterestChargingGrace());
|
||||
final Money realTotalInterestForLoan = totalInterestForLoanTerm.minus(totalInterestFree);
|
||||
|
||||
final Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.actualNumberOfRepayments);
|
||||
|
||||
|
||||
|
||||
|
||||
Integer interestPaymentDuePeriods = calculateNumberOfRemainingInterestPaymentPeriods(this.actualNumberOfRepayments,
|
||||
this.excludePeriodsForCalculation);
|
||||
interestForInstallment = realTotalInterestForLoan
|
||||
.dividedBy(BigDecimal.valueOf(interestPaymentDuePeriods), mc.getRoundingMode());
|
||||
if (this.excludePeriodsForCalculation < this.periodsCompleted) {
|
||||
Money interestLeft = this.totalInterestDue.minus(this.totalInterestAccounted);
|
||||
Integer interestDuePeriods = calculateNumberOfRemainingInterestPaymentPeriods(this.actualNumberOfRepayments,
|
||||
this.periodsCompleted);
|
||||
interestForInstallment = interestLeft.dividedBy(Long.valueOf(interestDuePeriods), mc.getRoundingMode());
|
||||
}
|
||||
if(!this.periodNumbersApplicableForInterestGrace.isEmpty()){
|
||||
int periodsElapsed = calculateLastInterestGracePeriod(periodNumber);
|
||||
if (periodsElapsed > this.excludePeriodsForCalculation && periodsElapsed > this.periodsCompleted) {
|
||||
Money interestLeft = this.totalInterestDue.minus(this.totalInterestAccounted);
|
||||
Integer interestDuePeriods = calculateNumberOfRemainingInterestPaymentPeriods(this.actualNumberOfRepayments,
|
||||
periodsElapsed);
|
||||
interestForInstallment = interestLeft.dividedBy(Long.valueOf(interestDuePeriods), mc.getRoundingMode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return interestForInstallment;
|
||||
@@ -1032,24 +1081,46 @@ public final class LoanApplicationTerms {
|
||||
|
||||
private int calculateNumberOfRepaymentsWithPrincipalPayment() {
|
||||
int numPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(this.actualNumberOfRepayments,
|
||||
this.getRecurringMoratoriumOnPrincipalPeriods(), this.getPrincipalGrace(), 0);
|
||||
this.periodsCompleted);
|
||||
// numPeriods = numPeriods - this.periodsCompleted;
|
||||
return numPeriods;
|
||||
}
|
||||
|
||||
private Integer calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(final Integer totalNumberOfRepaymentPeriods) {
|
||||
return totalNumberOfRepaymentPeriods - Math.max(getInterestChargingGrace(), getInterestPaymentGrace());
|
||||
|
||||
private Integer calculateNumberOfRemainingInterestPaymentPeriods(final Integer totalNumberOfRepaymentPeriods, int periodsElapsed) {
|
||||
int principalFeePeriods = 0;
|
||||
for (Integer intNumber : this.periodNumbersApplicableForInterestGrace) {
|
||||
if (intNumber > periodsElapsed) {
|
||||
principalFeePeriods++;
|
||||
}
|
||||
}
|
||||
Integer periodsRemaining = totalNumberOfRepaymentPeriods - periodsElapsed - principalFeePeriods;
|
||||
return periodsRemaining;
|
||||
}
|
||||
|
||||
private Integer calculateLastInterestGracePeriod(int periodNumber){
|
||||
int lastGracePeriod = 0;
|
||||
for (Integer grace : this.periodNumbersApplicableForInterestGrace) {
|
||||
if(grace < periodNumber && lastGracePeriod < grace){
|
||||
lastGracePeriod = grace;
|
||||
}
|
||||
}
|
||||
return lastGracePeriod;
|
||||
}
|
||||
|
||||
public boolean isPrincipalGraceApplicableForThisPeriod(final int periodNumber) {
|
||||
if (this.getRecurringMoratoriumOnPrincipalPeriods() > 0) {
|
||||
return ((periodNumber > 0 && periodNumber <= getPrincipalGrace()) || (periodNumber > 0 && (((periodNumber - getPrincipalGrace()) % (this
|
||||
.getRecurringMoratoriumOnPrincipalPeriods() + 1)) != 1)));
|
||||
boolean isPrincipalGraceApplicableForThisPeriod = false;
|
||||
if (this.periodNumbersApplicableForPrincipalGrace.contains(periodNumber)) {
|
||||
isPrincipalGraceApplicableForThisPeriod = true;
|
||||
}
|
||||
return periodNumber > 0 && periodNumber <= getPrincipalGrace();
|
||||
return isPrincipalGraceApplicableForThisPeriod;
|
||||
}
|
||||
|
||||
private boolean isInterestPaymentGraceApplicableForThisPeriod(final int periodNumber) {
|
||||
return periodNumber > 0 && periodNumber <= getInterestPaymentGrace();
|
||||
public boolean isInterestPaymentGraceApplicableForThisPeriod(final int periodNumber) {
|
||||
boolean isInterestPaymentGraceApplicableForThisPeriod = false;
|
||||
if (this.periodNumbersApplicableForInterestGrace.contains(periodNumber)) {
|
||||
isInterestPaymentGraceApplicableForThisPeriod = true;
|
||||
}
|
||||
return isInterestPaymentGraceApplicableForThisPeriod;
|
||||
}
|
||||
|
||||
private boolean isFirstPeriodAfterInterestPaymentGracePeriod(final int periodNumber) {
|
||||
@@ -1099,7 +1170,7 @@ public final class LoanApplicationTerms {
|
||||
final double principalDouble = balance.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
|
||||
|
||||
final Integer periodsRemaining = calculateNumberOfRemainingPrincipalPaymentPeriods(this.actualNumberOfRepayments,
|
||||
this.getRecurringMoratoriumOnPrincipalPeriods(), this.getPrincipalGrace(), periodsElapsed);
|
||||
periodsElapsed);
|
||||
|
||||
double installmentAmount = FinanicalFunctions.pmt(periodicInterestRate.doubleValue(), periodsRemaining.doubleValue(),
|
||||
principalDouble, futureValue, false);
|
||||
@@ -1165,7 +1236,7 @@ public final class LoanApplicationTerms {
|
||||
Money principal = this.principal;
|
||||
if (this.fixedPrincipalAmount == null) {
|
||||
final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(
|
||||
this.actualNumberOfRepayments, this.getRecurringMoratoriumOnPrincipalPeriods(), this.getPrincipalGrace(), periodNumber);
|
||||
this.actualNumberOfRepayments, periodNumber);
|
||||
principal = this.principal.dividedBy(numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
|
||||
this.fixedPrincipalAmount = principal.getAmount();
|
||||
}
|
||||
@@ -1179,21 +1250,19 @@ public final class LoanApplicationTerms {
|
||||
|
||||
public void updateFixedPrincipalAmount(final MathContext mc, final int periodNumber, final Money outstandingAmount) {
|
||||
final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(this.actualNumberOfRepayments,
|
||||
this.getRecurringMoratoriumOnPrincipalPeriods(), this.getPrincipalGrace(), periodNumber - 1);
|
||||
periodNumber - 1);
|
||||
Money principal = outstandingAmount.dividedBy(numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
|
||||
this.fixedPrincipalAmount = principal.getAmount();
|
||||
}
|
||||
|
||||
private static Integer calculateNumberOfRemainingPrincipalPaymentPeriods(final Integer totalNumberOfRepaymentPeriods,
|
||||
final int recurringMoratoriumOnPrincipalPeriods, final int PrincipalGrace, int periodsElapsed) {
|
||||
if (PrincipalGrace > periodsElapsed) {
|
||||
periodsElapsed = PrincipalGrace;
|
||||
}
|
||||
Integer periodsRemaining = totalNumberOfRepaymentPeriods - periodsElapsed;
|
||||
if (recurringMoratoriumOnPrincipalPeriods > 0) {
|
||||
double periodsRemainingDouble = ((double) periodsRemaining / ((double) (recurringMoratoriumOnPrincipalPeriods + 1)));
|
||||
periodsRemaining = (int) Math.ceil(periodsRemainingDouble);
|
||||
private Integer calculateNumberOfRemainingPrincipalPaymentPeriods(final Integer totalNumberOfRepaymentPeriods, int periodsElapsed) {
|
||||
int principalFeePeriods = 0;
|
||||
for (Integer intNumber : this.periodNumbersApplicableForPrincipalGrace) {
|
||||
if (intNumber > periodsElapsed) {
|
||||
principalFeePeriods++;
|
||||
}
|
||||
}
|
||||
Integer periodsRemaining = totalNumberOfRepaymentPeriods - periodsElapsed - principalFeePeriods;
|
||||
return periodsRemaining;
|
||||
}
|
||||
|
||||
@@ -1469,10 +1538,7 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
|
||||
public void updateTotalInterestDue(Money totalInterestDue) {
|
||||
|
||||
if (totalInterestDue != null) {
|
||||
this.totalInterestDue = totalInterestDue;
|
||||
}
|
||||
this.totalInterestDue = totalInterestDue;
|
||||
}
|
||||
|
||||
public ApplicationCurrency getApplicationCurrency() {
|
||||
@@ -1576,4 +1642,81 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
return disbursedAmount;
|
||||
}
|
||||
|
||||
public void updatePeriodNumberApplicableForPrincipalOrInterestGrace(final Integer periodsApplicationForGrace) {
|
||||
int applicablePeriodNumber = periodsApplicationForGrace;
|
||||
int graceOnPrincipal = defaultToZeroIfNull(this.principalGrace);
|
||||
int graceOnInterest = defaultToZeroIfNull(this.interestPaymentGrace);
|
||||
|
||||
while (graceOnPrincipal > 0 || graceOnInterest > 0) {
|
||||
if (graceOnPrincipal > 0) {
|
||||
this.periodNumbersApplicableForPrincipalGrace.add(applicablePeriodNumber);
|
||||
}
|
||||
if (graceOnInterest > 0) {
|
||||
this.periodNumbersApplicableForInterestGrace.add(applicablePeriodNumber);
|
||||
}
|
||||
applicablePeriodNumber++;
|
||||
graceOnPrincipal--;
|
||||
graceOnInterest--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the value to zero if the provided value is null
|
||||
*
|
||||
* @return integer value equal/greater than 0
|
||||
**/
|
||||
private Integer defaultToZeroIfNull(Integer value) {
|
||||
|
||||
return (value != null) ? value : 0;
|
||||
}
|
||||
|
||||
public void updateExcludePeriodsForCalculation(Integer excludePeriodsForCalculation){
|
||||
this.excludePeriodsForCalculation = excludePeriodsForCalculation;
|
||||
this.extraPeriods = 0;
|
||||
}
|
||||
|
||||
public Integer getActualNoOfRepaymnets() {
|
||||
return this.actualNumberOfRepayments;
|
||||
}
|
||||
|
||||
public Money getTotalInterestDue() {
|
||||
return this.totalInterestDue;
|
||||
}
|
||||
|
||||
private void updateRecurringMoratoriumOnPrincipalPeriods(Integer periodNumber) {
|
||||
Boolean isPrincipalGraceApplicableForThisPeriod = false;
|
||||
Integer numberOfRepayments = this.actualNumberOfRepayments;
|
||||
if (this.getRecurringMoratoriumOnPrincipalPeriods() > 0) {
|
||||
while (numberOfRepayments > 0) {
|
||||
isPrincipalGraceApplicableForThisPeriod = ((periodNumber > 0 && periodNumber <= getPrincipalGrace()) || (periodNumber > 0 && (((periodNumber - getPrincipalGrace()) % (this
|
||||
.getRecurringMoratoriumOnPrincipalPeriods() + 1)) != 1)));
|
||||
if (isPrincipalGraceApplicableForThisPeriod) {
|
||||
this.periodNumbersApplicableForPrincipalGrace.add(periodNumber);
|
||||
}
|
||||
numberOfRepayments--;
|
||||
periodNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void setTotalPrincipalAccounted(Money totalPrincipalAccounted) {
|
||||
this.totalPrincipalAccountedForInterestCalcualtion = totalPrincipalAccounted;
|
||||
}
|
||||
|
||||
|
||||
//Used for FLAT loans to calculate principal and interest per installment
|
||||
public void updateAccountedTillPeriod(int periodNumber, Money totalPrincipalAccounted,Money totalInterestAccounted, int extendPeriods){
|
||||
this.periodsCompleted = periodNumber;
|
||||
this.totalPrincipalAccounted = totalPrincipalAccounted;
|
||||
this.totalInterestAccounted = totalInterestAccounted;
|
||||
this.extraPeriods = this.extraPeriods + extendPeriods;
|
||||
}
|
||||
|
||||
public void updateTotalInterestAccounted(Money totalInterestAccounted){
|
||||
this.totalInterestAccounted = totalInterestAccounted;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-19
@@ -19,22 +19,15 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public interface LoanScheduleGenerator {
|
||||
@@ -42,18 +35,12 @@ public interface LoanScheduleGenerator {
|
||||
LoanScheduleModel generate(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
||||
final HolidayDetailDTO holidayDetailDTO);
|
||||
|
||||
LoanScheduleDTO rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
||||
final HolidayDetailDTO holidayDetailDTO, List<LoanTransaction> transactions,
|
||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, LocalDate rescheduleFrom);
|
||||
LoanScheduleDTO rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Loan loan,
|
||||
final HolidayDetailDTO holidayDetailDTO, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
LocalDate rescheduleFrom);
|
||||
|
||||
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(MonetaryCurrency currency, LocalDate onDate,
|
||||
LoanApplicationTerms loanApplicationTerms, MathContext mc, Set<LoanCharge> charges, HolidayDetailDTO holidayDetailDTO,
|
||||
List<LoanTransaction> loanTransactions, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
||||
LoanApplicationTerms loanApplicationTerms, MathContext mc, Loan loan, HolidayDetailDTO holidayDetailDTO,
|
||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor);
|
||||
|
||||
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance,
|
||||
CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, FloatingRateDTO floatingRateDTO,
|
||||
final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays);
|
||||
}
|
||||
+8
-12
@@ -86,7 +86,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.MinDaysBetweenDisbursalAndFirstRepaymentViolationException;
|
||||
@@ -621,9 +620,8 @@ public class LoanScheduleAssembler {
|
||||
}
|
||||
|
||||
public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId,
|
||||
List<LoanTransaction> transactions, final Set<LoanCharge> loanCharges,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom) {
|
||||
Loan loan, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final LocalDate rescheduleFrom) {
|
||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||
final MathContext mc = new MathContext(8, roundingMode);
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
@@ -634,15 +632,13 @@ public class LoanScheduleAssembler {
|
||||
|
||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, loanCharges, detailDTO, transactions,
|
||||
loanRepaymentScheduleTransactionProcessor, repaymentScheduleInstallments, rescheduleFrom).getLoanScheduleModel();
|
||||
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, loan, detailDTO,
|
||||
loanRepaymentScheduleTransactionProcessor, rescheduleFrom).getLoanScheduleModel();
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(MonetaryCurrency currency, LocalDate onDate,
|
||||
LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final Long officeId,
|
||||
List<LoanTransaction> loanTransactions,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments) {
|
||||
LoanApplicationTerms loanApplicationTerms, Loan loan, final Long officeId,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||
final MathContext mc = new MathContext(8, roundingMode);
|
||||
@@ -653,8 +649,8 @@ public class LoanScheduleAssembler {
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
return loanScheduleGenerator.calculatePrepaymentAmount(currency, onDate, loanApplicationTerms, mc, loanCharges, holidayDetailDTO,
|
||||
loanTransactions, loanRepaymentScheduleTransactionProcessor, repaymentScheduleInstallments);
|
||||
return loanScheduleGenerator.calculatePrepaymentAmount(currency, onDate, loanApplicationTerms, mc, loan, holidayDetailDTO,
|
||||
loanRepaymentScheduleTransactionProcessor);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -171,9 +171,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
}
|
||||
LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(loan);
|
||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(currency,
|
||||
today, loanApplicationTerms, loan.charges(), loan.getOfficeId(),
|
||||
loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor,
|
||||
loan.getRepaymentScheduleInstallments());
|
||||
today, loanApplicationTerms, loan, loan.getOfficeId(),
|
||||
loanRepaymentScheduleTransactionProcessor);
|
||||
Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus(
|
||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||
Money interestDue = Money.zero(currency);
|
||||
@@ -194,8 +193,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
}
|
||||
|
||||
LoanScheduleModel model = this.loanScheduleAssembler.assembleForInterestRecalculation(loanApplicationTerms, loan.getOfficeId(),
|
||||
modifiedTransactions, loan.charges(), loanRepaymentScheduleTransactionProcessor, loan.getRepaymentScheduleInstallments(),
|
||||
loan.fetchInterestRecalculateFromDate());
|
||||
loan, loanRepaymentScheduleTransactionProcessor, loan.fetchInterestRecalculateFromDate());
|
||||
LoanScheduleData scheduleDate = model.toData();
|
||||
Collection<LoanSchedulePeriodData> periodDatas = scheduleDate.getPeriods();
|
||||
for (LoanSchedulePeriodData periodData : periodDatas) {
|
||||
|
||||
+19
-3
@@ -42,9 +42,9 @@ import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSeria
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestData;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.service.LoanReschedulePreviewPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.service.LoanRescheduleRequestReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -108,14 +108,14 @@ public class RescheduleLoansApiResource {
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
if (compareIgnoreCase(command, "previewLoanReschedule")) {
|
||||
final LoanRescheduleModel loanRescheduleModel = this.loanReschedulePreviewPlatformService.previewLoanReschedule(scheduleId);
|
||||
final LoanScheduleModel loanRescheduleModel = this.loanReschedulePreviewPlatformService.previewLoanReschedule(scheduleId);
|
||||
|
||||
return this.loanRescheduleToApiJsonSerializer.serialize(settings, loanRescheduleModel.toData(), new HashSet<String>());
|
||||
}
|
||||
|
||||
final LoanRescheduleRequestData loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
|
||||
.readLoanRescheduleRequest(scheduleId);
|
||||
|
||||
|
||||
return this.loanRescheduleRequestToApiJsonSerializer.serialize(settings, loanRescheduleRequestData);
|
||||
}
|
||||
|
||||
@@ -170,4 +170,20 @@ public class RescheduleLoansApiResource {
|
||||
private boolean compareIgnoreCase(String firstString, String secondString) {
|
||||
return StringUtils.isNotBlank(firstString) && firstString.trim().equalsIgnoreCase(secondString);
|
||||
}
|
||||
|
||||
/*@GET
|
||||
@Path("{scheduleId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveTemplate(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
LoanRescheduleRequestData loanRescheduleReasons = null;
|
||||
loanRescheduleReasons = this.loanRescheduleRequestReadPlatformService
|
||||
.retrieveAllRescheduleReasons(RescheduleLoansApiConstants.LOAN_RESCHEDULE_REASON);
|
||||
|
||||
return this.loanRescheduleRequestToApiJsonSerializer.serialize(settings, loanRescheduleReasons);
|
||||
}*/
|
||||
}
|
||||
|
||||
+16
-55
@@ -18,10 +18,10 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
@@ -36,59 +36,55 @@ public class LoanRescheduleRequestData {
|
||||
private final String loanAccountNumber;
|
||||
private final LoanRescheduleRequestStatusEnumData statusEnum;
|
||||
private final Integer rescheduleFromInstallment;
|
||||
private final Integer graceOnPrincipal;
|
||||
private final Integer graceOnInterest;
|
||||
private final LocalDate rescheduleFromDate;
|
||||
private final Integer extraTerms;
|
||||
private final BigDecimal interestRate;
|
||||
private final Boolean recalculateInterest;
|
||||
private final CodeValueData rescheduleReasonCodeValue;
|
||||
private final LoanRescheduleRequestTimelineData timeline;
|
||||
private final String rescheduleReasonComment;
|
||||
private final LocalDate adjustedDueDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<CodeValueData> rescheduleReasons;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<LoanTermVariationsData> loanTermVariationsData;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestData constructor
|
||||
* @param loanTermVariationsData TODO
|
||||
**/
|
||||
private LoanRescheduleRequestData(Long id, Long loanId, LoanRescheduleRequestStatusEnumData statusEnum,
|
||||
Integer rescheduleFromInstallment, Integer graceOnPrincipal, Integer graceOnInterest, LocalDate rescheduleFromDate,
|
||||
LocalDate adjustedDueDate, Integer extraTerms, BigDecimal interestRate, CodeValueData rescheduleReasonCodeValue,
|
||||
Integer rescheduleFromInstallment, LocalDate rescheduleFromDate, CodeValueData rescheduleReasonCodeValue,
|
||||
String rescheduleReasonComment, LoanRescheduleRequestTimelineData timeline, final String clientName,
|
||||
final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest, Collection<CodeValueData> rescheduleReasons) {
|
||||
final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest,
|
||||
Collection<CodeValueData> rescheduleReasons, final Collection<LoanTermVariationsData> loanTermVariationsData) {
|
||||
|
||||
this.id = id;
|
||||
this.loanId = loanId;
|
||||
this.statusEnum = statusEnum;
|
||||
this.rescheduleFromInstallment = rescheduleFromInstallment;
|
||||
this.graceOnPrincipal = graceOnPrincipal;
|
||||
this.graceOnInterest = graceOnInterest;
|
||||
this.rescheduleFromDate = rescheduleFromDate;
|
||||
this.extraTerms = extraTerms;
|
||||
this.interestRate = interestRate;
|
||||
this.rescheduleReasonCodeValue = rescheduleReasonCodeValue;
|
||||
this.rescheduleReasonComment = rescheduleReasonComment;
|
||||
this.adjustedDueDate = adjustedDueDate;
|
||||
this.timeline = timeline;
|
||||
this.clientName = clientName;
|
||||
this.loanAccountNumber = loanAccountNumber;
|
||||
this.clientId = clientId;
|
||||
this.recalculateInterest = recalculateInterest;
|
||||
this.rescheduleReasons = rescheduleReasons ;
|
||||
this.loanTermVariationsData = loanTermVariationsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loanTermVariationsData TODO
|
||||
* @return an instance of the LoanRescheduleRequestData class
|
||||
**/
|
||||
public static LoanRescheduleRequestData instance(Long id, Long loanId, LoanRescheduleRequestStatusEnumData statusEnum,
|
||||
Integer rescheduleFromInstallment, Integer graceOnPrincipal, Integer graceOnInterest, LocalDate rescheduleFromDate,
|
||||
LocalDate adjustedDueDate, Integer extraTerms, BigDecimal interestRate, CodeValueData rescheduleReasonCodeValue,
|
||||
Integer rescheduleFromInstallment, LocalDate rescheduleFromDate, CodeValueData rescheduleReasonCodeValue,
|
||||
String rescheduleReasonComment, LoanRescheduleRequestTimelineData timeline, final String clientName,
|
||||
final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest, Collection<CodeValueData> rescheduleReasons) {
|
||||
final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest,
|
||||
Collection<CodeValueData> rescheduleReasons, final Collection<LoanTermVariationsData> loanTermVariationsData) {
|
||||
|
||||
return new LoanRescheduleRequestData(id, loanId, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
|
||||
rescheduleFromDate, adjustedDueDate, extraTerms, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment,
|
||||
timeline, clientName, loanAccountNumber, clientId, recalculateInterest, rescheduleReasons);
|
||||
return new LoanRescheduleRequestData(id, loanId, statusEnum, rescheduleFromInstallment, rescheduleFromDate,
|
||||
rescheduleReasonCodeValue, rescheduleReasonComment, timeline, clientName, loanAccountNumber, clientId, recalculateInterest,
|
||||
rescheduleReasons, loanTermVariationsData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,20 +115,6 @@ public class LoanRescheduleRequestData {
|
||||
return rescheduleFromInstallment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the graceOnPrincipal
|
||||
*/
|
||||
public Integer getGraceOnPrincipal() {
|
||||
return graceOnPrincipal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the graceOnInterest
|
||||
*/
|
||||
public Integer getGraceOnInterest() {
|
||||
return graceOnInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reschedule from date
|
||||
*/
|
||||
@@ -140,20 +122,6 @@ public class LoanRescheduleRequestData {
|
||||
return rescheduleFromDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the extraTerms
|
||||
*/
|
||||
public Integer getExtraTerms() {
|
||||
return extraTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the interestRate
|
||||
*/
|
||||
public BigDecimal getInterestRate() {
|
||||
return interestRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rescheduleReasonCodeValueId
|
||||
*/
|
||||
@@ -175,13 +143,6 @@ public class LoanRescheduleRequestData {
|
||||
return this.timeline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the adjustedDueDate
|
||||
*/
|
||||
public LocalDate getAdjustedDueDate() {
|
||||
return adjustedDueDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the clientName
|
||||
*/
|
||||
|
||||
+1
-30
@@ -32,10 +32,8 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.service.LoanRescheduleRequestReadPlatformService;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -47,13 +45,10 @@ import com.google.gson.reflect.TypeToken;
|
||||
public class LoanRescheduleRequestDataValidator {
|
||||
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
private final LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanRescheduleRequestDataValidator(FromJsonHelper fromJsonHelper,
|
||||
LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService) {
|
||||
public LoanRescheduleRequestDataValidator(FromJsonHelper fromJsonHelper) {
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
this.loanRescheduleRequestReadPlatformService = loanRescheduleRequestReadPlatformService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,21 +154,8 @@ public class LoanRescheduleRequestDataValidator {
|
||||
.failWithCode("repayment.schedule.installment.obligation.met", "Repayment schedule installment obligation met");
|
||||
}
|
||||
|
||||
if (installment != null && installment.isPartlyPaid()) {
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)
|
||||
.failWithCode("repayment.schedule.installment.partly.paid", "Repayment schedule installment is partly paid");
|
||||
}
|
||||
}
|
||||
|
||||
if (loanId != null) {
|
||||
List<LoanRescheduleRequestData> loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
|
||||
.readLoanRescheduleRequests(loanId, LoanStatus.APPROVED.getValue());
|
||||
|
||||
if (loanRescheduleRequestData.size() > 0) {
|
||||
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.already.rescheduled",
|
||||
"The loan can only be rescheduled once.");
|
||||
}
|
||||
}
|
||||
if(loan.isMultiDisburmentLoan()) {
|
||||
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(RescheduleLoansApiConstants.resheduleForMultiDisbursementNotSupportedErrorCode,
|
||||
"Loan rescheduling is not supported for multidisbursement loans");
|
||||
@@ -232,7 +214,6 @@ public class LoanRescheduleRequestDataValidator {
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
|
||||
if (loan != null) {
|
||||
Long loanId = loan.getId();
|
||||
|
||||
if (!loan.status().isActive()) {
|
||||
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.is.not.active", "Loan is not active");
|
||||
@@ -251,16 +232,6 @@ public class LoanRescheduleRequestDataValidator {
|
||||
"loan.repayment.schedule.installment." + "obligation.met", "Repayment schedule installment obligation met");
|
||||
}
|
||||
}
|
||||
|
||||
if (loanId != null) {
|
||||
List<LoanRescheduleRequestData> loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
|
||||
.readLoanRescheduleRequests(loanId, LoanStatus.APPROVED.getValue());
|
||||
|
||||
if (loanRescheduleRequestData.size() > 0) {
|
||||
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.already.rescheduled",
|
||||
"The loan can only be rescheduled once.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import java.math.MathContext;
|
||||
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.FlatInterestLoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
|
||||
public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory {
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
|
||||
final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
|
||||
final HolidayDetailDTO holidayDetailDTO, final CalendarInstance restCalendarInstance,
|
||||
final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, final FloatingRateDTO floatingRateDTO,
|
||||
final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays) {
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = null;
|
||||
|
||||
switch (interestMethod) {
|
||||
case DECLINING_BALANCE:
|
||||
loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
|
||||
applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar,
|
||||
floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays);
|
||||
break;
|
||||
|
||||
case FLAT:
|
||||
loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
|
||||
applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar,
|
||||
floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays);
|
||||
break;
|
||||
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return loanRescheduleModel;
|
||||
}
|
||||
}
|
||||
+103
-137
@@ -18,20 +18,27 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
@@ -40,116 +47,97 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
@Table(name = "m_loan_reschedule_request")
|
||||
public class LoanRescheduleRequest extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "loan_id", nullable = false)
|
||||
private Loan loan;
|
||||
|
||||
@Column(name = "status_enum", nullable = false)
|
||||
private Integer statusEnum;
|
||||
|
||||
@Column(name = "reschedule_from_installment")
|
||||
private Integer rescheduleFromInstallment;
|
||||
|
||||
@Column(name = "grace_on_principal")
|
||||
private Integer graceOnPrincipal;
|
||||
|
||||
@Column(name = "grace_on_interest")
|
||||
private Integer graceOnInterest;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "status_enum", nullable = false)
|
||||
private Integer statusEnum;
|
||||
|
||||
@Column(name = "reschedule_from_installment")
|
||||
private Integer rescheduleFromInstallment;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "reschedule_from_date")
|
||||
private Date rescheduleFromDate;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "adjusted_due_date")
|
||||
private Date adjustedDueDate;
|
||||
|
||||
@Column(name = "extra_terms")
|
||||
private Integer extraTerms;
|
||||
|
||||
@Column(name = "recalculate_interest")
|
||||
private Boolean recalculateInterest;
|
||||
|
||||
@Column(name = "interest_rate", scale = 6, precision = 19)
|
||||
private BigDecimal interestRate;
|
||||
|
||||
@ManyToOne
|
||||
private Date rescheduleFromDate;
|
||||
|
||||
@Column(name = "recalculate_interest")
|
||||
private Boolean recalculateInterest;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "reschedule_reason_cv_id")
|
||||
private CodeValue rescheduleReasonCodeValue;
|
||||
|
||||
@Column(name = "reschedule_reason_comment")
|
||||
private String rescheduleReasonComment;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
private CodeValue rescheduleReasonCodeValue;
|
||||
|
||||
@Column(name = "reschedule_reason_comment")
|
||||
private String rescheduleReasonComment;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "submitted_on_date")
|
||||
private Date submittedOnDate;
|
||||
|
||||
@ManyToOne
|
||||
private Date submittedOnDate;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "submitted_by_user_id")
|
||||
private AppUser submittedByUser;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
private AppUser submittedByUser;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "approved_on_date")
|
||||
private Date approvedOnDate;
|
||||
private Date approvedOnDate;
|
||||
|
||||
@ManyToOne
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "approved_by_user_id")
|
||||
private AppUser approvedByUser;
|
||||
private AppUser approvedByUser;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "rejected_on_date")
|
||||
private Date rejectedOnDate;
|
||||
private Date rejectedOnDate;
|
||||
|
||||
@ManyToOne
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "rejected_by_user_id")
|
||||
private AppUser rejectedByUser;
|
||||
private AppUser rejectedByUser;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.EAGER)
|
||||
@JoinColumn(name = "loan_reschedule_request_id", nullable = false)
|
||||
private Set<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings = new HashSet<>();
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequest constructor
|
||||
**/
|
||||
protected LoanRescheduleRequest() {}
|
||||
/**
|
||||
* LoanRescheduleRequest constructor
|
||||
**/
|
||||
protected LoanRescheduleRequest() {}
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequest constructor
|
||||
**/
|
||||
private LoanRescheduleRequest(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment,
|
||||
final Date rescheduleFromDate, final Boolean recalculateInterest, final CodeValue rescheduleReasonCodeValue,
|
||||
final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser, final Date approvedOnDate,
|
||||
final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
|
||||
this.loan = loan;
|
||||
this.statusEnum = statusEnum;
|
||||
this.rescheduleFromInstallment = rescheduleFromInstallment;
|
||||
this.rescheduleFromDate = rescheduleFromDate;
|
||||
this.rescheduleReasonCodeValue = rescheduleReasonCodeValue;
|
||||
this.rescheduleReasonComment = rescheduleReasonComment;
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
this.submittedByUser = submittedByUser;
|
||||
this.approvedOnDate = approvedOnDate;
|
||||
this.approvedByUser = approvedByUser;
|
||||
this.rejectedOnDate = rejectedOnDate;
|
||||
this.rejectedByUser = rejectedByUser;
|
||||
this.recalculateInterest = recalculateInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequest constructor
|
||||
**/
|
||||
private LoanRescheduleRequest(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment,
|
||||
final Integer graceOnPrincipal, final Integer graceOnInterest, final Date rescheduleFromDate, final Date adjustedDueDate,
|
||||
final Integer extraTerms, final Boolean recalculateInterest, final BigDecimal interestRate, final CodeValue rescheduleReasonCodeValue,
|
||||
final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser,
|
||||
final Date approvedOnDate, final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
|
||||
this.loan = loan;
|
||||
this.statusEnum = statusEnum;
|
||||
this.rescheduleFromInstallment = rescheduleFromInstallment;
|
||||
this.graceOnPrincipal = graceOnPrincipal;
|
||||
this.graceOnInterest = graceOnInterest;
|
||||
this.rescheduleFromDate = rescheduleFromDate;
|
||||
this.extraTerms = extraTerms;
|
||||
this.interestRate = interestRate;
|
||||
this.rescheduleReasonCodeValue = rescheduleReasonCodeValue;
|
||||
this.rescheduleReasonComment = rescheduleReasonComment;
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
this.submittedByUser = submittedByUser;
|
||||
this.approvedOnDate = approvedOnDate;
|
||||
this.approvedByUser = approvedByUser;
|
||||
this.rejectedOnDate = rejectedOnDate;
|
||||
this.rejectedByUser = rejectedByUser;
|
||||
this.adjustedDueDate = adjustedDueDate;
|
||||
this.recalculateInterest = recalculateInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new instance of the LoanRescheduleRequest class
|
||||
**/
|
||||
public static LoanRescheduleRequest instance(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment,
|
||||
final Integer graceOnPrincipal, final Integer graceOnInterest, final Date rescheduleFromDate, final Date adjustedDueDate,
|
||||
final Integer extraTerms, final Boolean recalculateInterest, final BigDecimal interestRate, final CodeValue rescheduleReasonCodeValue,
|
||||
final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser,
|
||||
final Date approvedOnDate, final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
|
||||
|
||||
return new LoanRescheduleRequest(loan, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
|
||||
rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment, submittedOnDate,
|
||||
submittedByUser, approvedOnDate, approvedByUser, rejectedOnDate, rejectedByUser);
|
||||
}
|
||||
/**
|
||||
* @return a new instance of the LoanRescheduleRequest class
|
||||
**/
|
||||
public static LoanRescheduleRequest instance(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment,
|
||||
final Date rescheduleFromDate, final Boolean recalculateInterest, final CodeValue rescheduleReasonCodeValue,
|
||||
final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser, final Date approvedOnDate,
|
||||
final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
|
||||
|
||||
return new LoanRescheduleRequest(loan, statusEnum, rescheduleFromInstallment, rescheduleFromDate, recalculateInterest,
|
||||
rescheduleReasonCodeValue, rescheduleReasonComment, submittedOnDate, submittedByUser, approvedOnDate, approvedByUser,
|
||||
rejectedOnDate, rejectedByUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reschedule request loan object
|
||||
@@ -172,20 +160,6 @@ public class LoanRescheduleRequest extends AbstractPersistable<Long> {
|
||||
return this.rescheduleFromInstallment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the grace on principal
|
||||
**/
|
||||
public Integer getGraceOnPrincipal() {
|
||||
return this.graceOnPrincipal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the grace on interest
|
||||
**/
|
||||
public Integer getGraceOnInterest() {
|
||||
return this.graceOnInterest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return due date of the rescheduling start point
|
||||
**/
|
||||
@@ -200,34 +174,6 @@ public class LoanRescheduleRequest extends AbstractPersistable<Long> {
|
||||
return localDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return due date of the first rescheduled installment
|
||||
**/
|
||||
public LocalDate getAdjustedDueDate() {
|
||||
|
||||
LocalDate localDate = null;
|
||||
|
||||
if(this.adjustedDueDate != null) {
|
||||
localDate = new LocalDate(this.adjustedDueDate);
|
||||
}
|
||||
|
||||
return localDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return extra terms to be added after the last loan installment
|
||||
**/
|
||||
public Integer getExtraTerms() {
|
||||
return this.extraTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the new interest rate to be applied to unpaid installments
|
||||
**/
|
||||
public BigDecimal getInterestRate() {
|
||||
return this.interestRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the reschedule reason code value object
|
||||
**/
|
||||
@@ -348,4 +294,24 @@ public class LoanRescheduleRequest extends AbstractPersistable<Long> {
|
||||
this.statusEnum = LoanStatus.REJECTED.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLoanRescheduleRequestToTermVariationMappings(final List<LoanRescheduleRequestToTermVariationMapping> mapping) {
|
||||
this.loanRescheduleRequestToTermVariationMappings.addAll(mapping);
|
||||
}
|
||||
|
||||
public Set<LoanRescheduleRequestToTermVariationMapping> getLoanRescheduleRequestToTermVariationMappings() {
|
||||
return this.loanRescheduleRequestToTermVariationMappings;
|
||||
}
|
||||
|
||||
public LoanTermVariations getDueDateTermVariationIfExists() {
|
||||
if(this.loanRescheduleRequestToTermVariationMappings != null
|
||||
&& this.loanRescheduleRequestToTermVariationMappings.size() > 0){
|
||||
for (LoanRescheduleRequestToTermVariationMapping mapping : this.loanRescheduleRequestToTermVariationMappings) {
|
||||
if(mapping.getLoanTermVariations().getTermType().isDueDateVariation()){
|
||||
return mapping.getLoanTermVariations();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* 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.rescheduleloan.domain;
|
||||
|
||||
import java.math.MathContext;
|
||||
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
|
||||
public interface LoanReschedulerFactory {
|
||||
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
|
||||
final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
|
||||
final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance,
|
||||
final Calendar loanCalendar, FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst,
|
||||
final Integer numberofdays);
|
||||
}
|
||||
+2
-2
@@ -18,9 +18,9 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.service;
|
||||
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
|
||||
public interface LoanReschedulePreviewPlatformService {
|
||||
|
||||
public LoanRescheduleModel previewLoanReschedule(Long requestId);
|
||||
public LoanScheduleModel previewLoanReschedule(Long requestId);
|
||||
}
|
||||
|
||||
+88
-105
@@ -18,41 +18,33 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayStatusType;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException;
|
||||
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -60,103 +52,94 @@ import org.springframework.stereotype.Service;
|
||||
public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService {
|
||||
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||
private final LoanSummaryWrapper loanSummaryWrapper;
|
||||
private final DefaultScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
|
||||
@Autowired
|
||||
public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
|
||||
final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService, final LoanUtilService loanUtilService) {
|
||||
final LoanUtilService loanUtilService,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final LoanSummaryWrapper loanSummaryWrapper) {
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
this.loanUtilService = loanUtilService;
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
this.loanSummaryWrapper = loanSummaryWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel previewLoanReschedule(Long requestId) {
|
||||
public LoanScheduleModel previewLoanReschedule(Long requestId) {
|
||||
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(requestId);
|
||||
|
||||
if (loanRescheduleRequest == null) { throw new LoanRescheduleRequestNotFoundException(requestId); }
|
||||
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
|
||||
.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan,
|
||||
loanRescheduleRequest.getRescheduleFromDate());
|
||||
LocalDate rescheduleFromDate = null;
|
||||
List<LoanTermVariationsData> removeLoanTermVariationsData = new ArrayList<>();
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
LoanTermVariations dueDateVariationInCurrentRequest = loanRescheduleRequest.getDueDateTermVariationIfExists();
|
||||
if(dueDateVariationInCurrentRequest != null){
|
||||
for (LoanTermVariationsData loanTermVariation : loanApplicationTerms.getLoanTermVariations().getDueDateVariation()) {
|
||||
if (loanTermVariation.getDateValue().equals(dueDateVariationInCurrentRequest.fetchTermApplicaDate())) {
|
||||
rescheduleFromDate = loanTermVariation.getTermApplicableFrom();
|
||||
removeLoanTermVariationsData.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
}
|
||||
loanApplicationTerms.getLoanTermVariations().getDueDateVariation().removeAll(removeLoanTermVariationsData);
|
||||
if (rescheduleFromDate == null) {
|
||||
rescheduleFromDate = loanRescheduleRequest.getRescheduleFromDate();
|
||||
}
|
||||
List<LoanTermVariationsData> loanTermVariationsData = new ArrayList<>();
|
||||
LocalDate adjustedApplicableDate = null;
|
||||
Set<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings = loanRescheduleRequest.getLoanRescheduleRequestToTermVariationMappings();
|
||||
if (!loanRescheduleRequestToTermVariationMappings.isEmpty()) {
|
||||
for (LoanRescheduleRequestToTermVariationMapping loanRescheduleRequestToTermVariationMapping : loanRescheduleRequestToTermVariationMappings) {
|
||||
if (loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().getTermType().isDueDateVariation()
|
||||
&& rescheduleFromDate != null) {
|
||||
adjustedApplicableDate = loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().fetchDateValue();
|
||||
loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().setTermApplicableFrom(
|
||||
rescheduleFromDate.toDate());
|
||||
}
|
||||
loanTermVariationsData.add(loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().toData());
|
||||
}
|
||||
}
|
||||
|
||||
for (LoanTermVariationsData loanTermVariation : loanApplicationTerms.getLoanTermVariations().getDueDateVariation()) {
|
||||
if (rescheduleFromDate.isBefore(loanTermVariation.getTermApplicableFrom())) {
|
||||
LocalDate applicableDate = this.scheduledDateGenerator.generateNextRepaymentDate(rescheduleFromDate, loanApplicationTerms,
|
||||
false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
if (loanTermVariation.getTermApplicableFrom().equals(applicableDate)) {
|
||||
LocalDate adjustedDate = this.scheduledDateGenerator.generateNextRepaymentDate(adjustedApplicableDate,
|
||||
loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
loanTermVariation.setApplicableFromDate(adjustedDate);
|
||||
loanTermVariationsData.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loanApplicationTerms.getLoanTermVariations().updateLoanTermVariationsData(loanTermVariationsData);
|
||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive(
|
||||
loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
}
|
||||
final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
Calendar loanCalendar = null;
|
||||
if (loanCalendarInstance != null) {
|
||||
loanCalendar = loanCalendarInstance.getCalendar();
|
||||
}
|
||||
final FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
Boolean isSkipRepaymentOnFirstMonth = false;
|
||||
Integer numberOfDays = 0;
|
||||
boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
|
||||
if(isSkipRepaymentOnFirstMonthEnabled){
|
||||
isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), loanCalendar);
|
||||
if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); }
|
||||
|
||||
}
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||
loanCalendar, floatingRateDTO, isSkipRepaymentOnFirstMonth, numberOfDays);
|
||||
LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel,
|
||||
oldPeriods);
|
||||
return loanRescheduleModelWithOldPeriods;
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = null;
|
||||
try{
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
}catch(final FloatingRateNotFoundException ex){
|
||||
// Do not do anything
|
||||
}
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
|
||||
loan.setHelpers(loanLifecycleStateMachine, this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms,
|
||||
loan, loanApplicationTerms.getHolidayDetailDTO(),
|
||||
loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
||||
final LoanScheduleModel loanScheduleModel = loanSchedule.getLoanScheduleModel();
|
||||
LoanScheduleModel loanScheduleModels = LoanScheduleModel.withLoanScheduleModelPeriods(loanScheduleModel.getPeriods(),
|
||||
loanScheduleModel);
|
||||
|
||||
return loanScheduleModels;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+46
-23
@@ -29,6 +29,7 @@ import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
@@ -36,6 +37,7 @@ import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanResched
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestEnumerations;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestStatusEnumData;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestTimelineData;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
@@ -71,13 +73,8 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
|
||||
sqlBuilder.append("mc.id as clientId, ");
|
||||
sqlBuilder.append("ml.account_no as loanAccountNumber, ");
|
||||
sqlBuilder.append("lr.reschedule_from_installment as rescheduleFromInstallment, ");
|
||||
sqlBuilder.append("lr.grace_on_principal as graceOnPrincipal, ");
|
||||
sqlBuilder.append("lr.grace_on_interest as graceOnInterest, ");
|
||||
sqlBuilder.append("lr.reschedule_from_date as rescheduleFromDate, ");
|
||||
sqlBuilder.append("lr.adjusted_due_date as adjustedDueDate, ");
|
||||
sqlBuilder.append("lr.extra_terms as extraTerms, ");
|
||||
sqlBuilder.append("lr.recalculate_interest as recalculateInterest, ");
|
||||
sqlBuilder.append("lr.interest_rate as interestRate, ");
|
||||
sqlBuilder.append("lr.reschedule_reason_cv_id as rescheduleReasonCvId, ");
|
||||
sqlBuilder.append("cv.code_value as rescheduleReasonCvValue, ");
|
||||
sqlBuilder.append("lr.reschedule_reason_comment as rescheduleReasonComment, ");
|
||||
@@ -95,7 +92,14 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
|
||||
sqlBuilder.append("lr.rejected_on_date as rejectedOnDate, ");
|
||||
sqlBuilder.append("rbu.username as rejectedByUsername, ");
|
||||
sqlBuilder.append("rbu.firstname as rejectedByFirstname, ");
|
||||
sqlBuilder.append("rbu.lastname as rejectedByLastname ");
|
||||
sqlBuilder.append("rbu.lastname as rejectedByLastname, ");
|
||||
|
||||
sqlBuilder.append("tv.id as termId,");
|
||||
sqlBuilder.append("tv.term_type as termType,");
|
||||
sqlBuilder.append("tv.applicable_date as variationApplicableFrom, ");
|
||||
sqlBuilder.append("tv.decimal_value as decimalValue, ");
|
||||
sqlBuilder.append("tv.date_value as dateValue, ");
|
||||
sqlBuilder.append("tv.is_specific_to_installment as isSpecificToInstallment ");
|
||||
|
||||
sqlBuilder.append("from " + loanRescheduleRequestTableName() + " lr ");
|
||||
sqlBuilder.append("left join m_code_value cv on cv.id = lr.reschedule_reason_cv_id ");
|
||||
@@ -104,6 +108,8 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
|
||||
sqlBuilder.append("left join m_appuser rbu on rbu.id = lr.rejected_by_user_id ");
|
||||
sqlBuilder.append("left join m_loan ml on ml.id = lr.loan_id ");
|
||||
sqlBuilder.append("left join m_client mc on mc.id = ml.client_id ");
|
||||
sqlBuilder.append("join m_loan_reschedule_request_term_variations_mapping rrtvm on lr.id = rrtvm.loan_reschedule_request_id ");
|
||||
sqlBuilder.append("join m_loan_term_variations tv on tv.id = rrtvm.loan_term_variations_id and tv.parent_id is null") ;
|
||||
|
||||
this.schema = sqlBuilder.toString();
|
||||
}
|
||||
@@ -127,12 +133,7 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
|
||||
final String loanAccountNumber = rs.getString("loanAccountNumber");
|
||||
final Long clientId = rs.getLong("clientId");
|
||||
final Integer rescheduleFromInstallment = JdbcSupport.getInteger(rs, "rescheduleFromInstallment");
|
||||
final Integer graceOnPrincipal = JdbcSupport.getInteger(rs, "graceOnPrincipal");
|
||||
final Integer graceOnInterest = JdbcSupport.getInteger(rs, "graceOnInterest");
|
||||
final LocalDate rescheduleFromDate = JdbcSupport.getLocalDate(rs, "rescheduleFromDate");
|
||||
final LocalDate adjustedDueDate = JdbcSupport.getLocalDate(rs, "adjustedDueDate");
|
||||
final Integer extraTerms = JdbcSupport.getInteger(rs, "extraTerms");
|
||||
final BigDecimal interestRate = rs.getBigDecimal("interestRate");
|
||||
final Long rescheduleReasonCvId = JdbcSupport.getLong(rs, "rescheduleReasonCvId");
|
||||
final String rescheduleReasonCvValue = rs.getString("rescheduleReasonCvValue");
|
||||
final CodeValueData rescheduleReasonCodeValue = CodeValueData.instance(rescheduleReasonCvId, rescheduleReasonCvValue);
|
||||
@@ -157,14 +158,40 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
|
||||
final LoanRescheduleRequestTimelineData timeline = new LoanRescheduleRequestTimelineData(submittedOnDate, submittedByUsername,
|
||||
submittedByFirstname, submittedByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname,
|
||||
rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname);
|
||||
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
do {
|
||||
Long tempId = rs.getLong("id");
|
||||
if (id.equals(tempId)) {
|
||||
loanTermVariations.add(fetchLoanTermVariation(rs));
|
||||
} else {
|
||||
rs.previous();
|
||||
break;
|
||||
}
|
||||
} while (rs.next());
|
||||
|
||||
return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
|
||||
rescheduleFromDate, adjustedDueDate, extraTerms, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment,
|
||||
timeline, clientName, loanAccountNumber, clientId, recalculateInterest, rescheduleReasons);
|
||||
return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, rescheduleFromDate,
|
||||
rescheduleReasonCodeValue, rescheduleReasonComment, timeline, clientName, loanAccountNumber, clientId,
|
||||
recalculateInterest, rescheduleReasons, loanTermVariations);
|
||||
}
|
||||
|
||||
private LoanTermVariationsData fetchLoanTermVariation(final ResultSet rs) throws SQLException {
|
||||
final Long id = rs.getLong("termId");
|
||||
final LocalDate variationApplicableFrom = JdbcSupport.getLocalDate(rs, "variationApplicableFrom");
|
||||
final BigDecimal decimalValue = rs.getBigDecimal("decimalValue");
|
||||
final LocalDate dateValue = JdbcSupport.getLocalDate(rs, "dateValue");
|
||||
final boolean isSpecificToInstallment = rs.getBoolean("isSpecificToInstallment");
|
||||
final int termType = rs.getInt("termType");
|
||||
|
||||
final LoanTermVariationsData loanTermVariationsData = new LoanTermVariationsData(id,
|
||||
LoanEnumerations.loanvariationType(termType), variationApplicableFrom, decimalValue, dateValue,
|
||||
isSpecificToInstallment);
|
||||
return loanTermVariationsData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<LoanRescheduleRequestData> readLoanRescheduleRequests(Long loanId) {
|
||||
final Loan loan = this.loanRepository.findOne(loanId);
|
||||
@@ -209,12 +236,7 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
|
||||
final Long loanId = null;
|
||||
final LoanRescheduleRequestStatusEnumData statusEnum = null;
|
||||
final Integer rescheduleFromInstallment = null;
|
||||
final Integer graceOnPrincipal = null;
|
||||
final Integer graceOnInterest = null;
|
||||
final LocalDate rescheduleFromDate = null;
|
||||
final LocalDate adjustedDueDate = null;
|
||||
final Integer extraTerms = null;
|
||||
final BigDecimal interestRate = null;
|
||||
final CodeValueData rescheduleReasonCodeValue = null;
|
||||
final String rescheduleReasonComment = null;
|
||||
final LoanRescheduleRequestTimelineData timeline = null;
|
||||
@@ -222,9 +244,10 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
|
||||
final String loanAccountNumber = null;
|
||||
final Long clientId = null;
|
||||
final Boolean recalculateInterest = null;
|
||||
final Collection<LoanTermVariationsData> loanTermVariationsData = null;
|
||||
|
||||
return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
|
||||
rescheduleFromDate, adjustedDueDate, extraTerms, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment,
|
||||
timeline, clientName, loanAccountNumber, clientId, recalculateInterest, rescheduleReasons);
|
||||
return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, rescheduleFromDate,
|
||||
rescheduleReasonCodeValue, rescheduleReasonComment, timeline, clientName, loanAccountNumber, clientId, recalculateInterest,
|
||||
rescheduleReasons, loanTermVariationsData);
|
||||
}
|
||||
}
|
||||
|
||||
+236
-250
@@ -22,9 +22,9 @@ import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -33,64 +33,52 @@ import java.util.Set;
|
||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException;
|
||||
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
@@ -107,25 +95,25 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
|
||||
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
private final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator;
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepositoryWrapper holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
|
||||
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||
private final LoanRepository loanRepository;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||
private final LoanSummaryWrapper loanSummaryWrapper;
|
||||
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
||||
private final DefaultScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
private final LoanAccountDomainService loanAccountDomainService;
|
||||
private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestWritePlatformServiceImpl constructor
|
||||
@@ -133,38 +121,39 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
* @return void
|
||||
**/
|
||||
@Autowired
|
||||
public LoanRescheduleRequestWritePlatformServiceImpl(LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
CodeValueRepositoryWrapper codeValueRepositoryWrapper, PlatformSecurityContext platformSecurityContext,
|
||||
LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
|
||||
LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, ConfigurationDomainService configurationDomainService,
|
||||
HolidayRepositoryWrapper holidayRepository, WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
|
||||
public LoanRescheduleRequestWritePlatformServiceImpl(final CodeValueRepositoryWrapper codeValueRepositoryWrapper,
|
||||
final PlatformSecurityContext platformSecurityContext,
|
||||
final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
|
||||
final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
|
||||
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
|
||||
final CalendarInstanceRepository calendarInstanceRepository, final LoanChargeReadPlatformService loanChargeReadPlatformService,
|
||||
final LoanTransactionRepository loanTransactionRepository,
|
||||
final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanRepository loanRepository,
|
||||
final LoanAssembler loanAssembler, final FloatingRatesReadPlatformService floatingRatesReadPlatformService,
|
||||
final LoanUtilService loanUtilService) {
|
||||
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
||||
final LoanAssembler loanAssembler, final LoanUtilService loanUtilService,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final LoanSummaryWrapper loanSummaryWrapper,
|
||||
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
|
||||
final LoanAccountDomainService loanAccountDomainService,
|
||||
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository) {
|
||||
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
|
||||
this.platformSecurityContext = platformSecurityContext;
|
||||
this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
|
||||
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.loanChargeReadPlatformService = loanChargeReadPlatformService;
|
||||
this.loanTransactionRepository = loanTransactionRepository;
|
||||
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanAssembler = loanAssembler;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
this.loanUtilService = loanUtilService;
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
this.loanSummaryWrapper = loanSummaryWrapper;
|
||||
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
|
||||
this.loanAccountDomainService = loanAccountDomainService;
|
||||
this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,7 +171,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
final Long loanId = jsonCommand.longValueOfParameterNamed(RescheduleLoansApiConstants.loanIdParamName);
|
||||
|
||||
// use the loan id to get a Loan entity object
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
|
||||
// validate the request in the JsonCommand object passed as
|
||||
// parameter
|
||||
@@ -275,12 +264,24 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
}
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = LoanRescheduleRequest.instance(loan,
|
||||
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
|
||||
rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue,
|
||||
rescheduleReasonComment, submittedOnDate, this.platformSecurityContext.authenticatedUser(), null, null, null, null);
|
||||
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), rescheduleFromInstallment, rescheduleFromDate,
|
||||
recalculateInterest, rescheduleReasonCodeValue, rescheduleReasonComment, submittedOnDate,
|
||||
this.platformSecurityContext.authenticatedUser(), null, null, null, null);
|
||||
|
||||
// update reschedule request to term variations mapping
|
||||
List<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings = new ArrayList<>();
|
||||
final Boolean isActive = false;
|
||||
final boolean isSpecificToInstallment = false;
|
||||
BigDecimal decimalValue = null;
|
||||
Date dueDate = null;
|
||||
// create term variations for flat and declining balance loans
|
||||
createLoanTermVariationsForRegularLoans(loan, graceOnPrincipal, graceOnInterest, extraTerms, interestRate, rescheduleFromDate,
|
||||
adjustedDueDate, loanRescheduleRequest, loanRescheduleRequestToTermVariationMappings, isActive,
|
||||
isSpecificToInstallment, decimalValue, dueDate);
|
||||
|
||||
// create a new entry in the m_loan_reschedule_request table
|
||||
this.loanRescheduleRequestRepository.save(loanRescheduleRequest);
|
||||
this.loanRepository.save(loan);
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequest.getId())
|
||||
.withLoanId(loan.getId()).build();
|
||||
@@ -295,6 +296,63 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
}
|
||||
}
|
||||
|
||||
private void createLoanTermVariationsForRegularLoans(final Loan loan, final Integer graceOnPrincipal, final Integer graceOnInterest,
|
||||
final Integer extraTerms, final BigDecimal interestRate, Date rescheduleFromDate, Date adjustedDueDate,
|
||||
final LoanRescheduleRequest loanRescheduleRequest,
|
||||
List<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings, final Boolean isActive,
|
||||
final boolean isSpecificToInstallment, BigDecimal decimalValue, Date dueDate) {
|
||||
|
||||
if (rescheduleFromDate != null && adjustedDueDate != null) {
|
||||
LoanTermVariations parent = null;
|
||||
final Integer termType = LoanTermVariationType.DUE_DATE.getValue();
|
||||
createLoanTermVariations(termType, loan, rescheduleFromDate, adjustedDueDate, loanRescheduleRequestToTermVariationMappings,
|
||||
isActive, isSpecificToInstallment, decimalValue, parent);
|
||||
}
|
||||
|
||||
if (rescheduleFromDate != null && interestRate != null) {
|
||||
LoanTermVariations parent = null;
|
||||
final Integer termType = LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getValue();
|
||||
createLoanTermVariations(termType, loan, rescheduleFromDate, dueDate, loanRescheduleRequestToTermVariationMappings, isActive,
|
||||
isSpecificToInstallment, interestRate, parent);
|
||||
}
|
||||
|
||||
if (rescheduleFromDate != null && graceOnPrincipal != null) {
|
||||
final Integer termType = LoanTermVariationType.GRACE_ON_PRINCIPAL.getValue();
|
||||
LoanTermVariations parent = null;
|
||||
parent = createLoanTermVariations(termType, loan, rescheduleFromDate, dueDate, loanRescheduleRequestToTermVariationMappings,
|
||||
isActive, isSpecificToInstallment, BigDecimal.valueOf(graceOnPrincipal), parent);
|
||||
|
||||
BigDecimal extraTermsBasedOnGracePeriods = BigDecimal.valueOf(graceOnPrincipal);
|
||||
createLoanTermVariations(LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue(), loan, rescheduleFromDate, dueDate,
|
||||
loanRescheduleRequestToTermVariationMappings, isActive, isSpecificToInstallment, extraTermsBasedOnGracePeriods, parent);
|
||||
|
||||
}
|
||||
|
||||
if (rescheduleFromDate != null && graceOnInterest != null) {
|
||||
LoanTermVariations parent = null;
|
||||
final Integer termType = LoanTermVariationType.GRACE_ON_INTEREST.getValue();
|
||||
createLoanTermVariations(termType, loan, rescheduleFromDate, dueDate, loanRescheduleRequestToTermVariationMappings, isActive,
|
||||
isSpecificToInstallment, BigDecimal.valueOf(graceOnInterest), parent);
|
||||
}
|
||||
|
||||
if (rescheduleFromDate != null && extraTerms != null) {
|
||||
LoanTermVariations parent = null;
|
||||
final Integer termType = LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue();
|
||||
createLoanTermVariations(termType, loan, rescheduleFromDate, dueDate, loanRescheduleRequestToTermVariationMappings, isActive,
|
||||
isSpecificToInstallment, BigDecimal.valueOf(extraTerms), parent);
|
||||
}
|
||||
loanRescheduleRequest.updateLoanRescheduleRequestToTermVariationMappings(loanRescheduleRequestToTermVariationMappings);
|
||||
}
|
||||
|
||||
private LoanTermVariations createLoanTermVariations(final Integer termType, final Loan loan, Date rescheduleFromDate,
|
||||
Date adjustedDueDate, List<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings,
|
||||
final Boolean isActive, final boolean isSpecificToInstallment, final BigDecimal decimalValue, LoanTermVariations parent) {
|
||||
LoanTermVariations loanTermVariation = new LoanTermVariations(termType, rescheduleFromDate, decimalValue, adjustedDueDate,
|
||||
isSpecificToInstallment, loan, loan.status().getValue(), isActive, parent);
|
||||
loanRescheduleRequestToTermVariationMappings.add(LoanRescheduleRequestToTermVariationMapping.createNew(loanTermVariation));
|
||||
return loanTermVariation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult approve(JsonCommand jsonCommand) {
|
||||
@@ -322,132 +380,112 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
changes.put("approvedOnDate", approvedOnDate.toString(dateTimeFormatter));
|
||||
changes.put("approvedByUserId", appUser.getId());
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
|
||||
.getDisbursementDate().toDate());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan,
|
||||
loanRescheduleRequest.getRescheduleFromDate());
|
||||
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
|
||||
.createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
|
||||
.createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
}
|
||||
final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
Calendar loanCalendar = null;
|
||||
if (loanCalendarInstance != null) {
|
||||
loanCalendar = loanCalendarInstance.getCalendar();
|
||||
}
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
Boolean isSkipRepaymentOnFirstMonth = false;
|
||||
Integer numberOfDays = 0;
|
||||
boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
|
||||
if(isSkipRepaymentOnFirstMonthEnabled){
|
||||
isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), loanCalendar);
|
||||
if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); }
|
||||
|
||||
}
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||
loanCalendar, floatingRateDTO, isSkipRepaymentOnFirstMonth, numberOfDays);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
Collection<LoanCharge> waiveLoanCharges = new ArrayList<>();
|
||||
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
|
||||
for (LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if (period.isNew()) {
|
||||
LoanRepaymentScheduleInstallment repaymentScheduleInstallment = new LoanRepaymentScheduleInstallment(loan,
|
||||
period.periodNumber(), period.periodFromDate(), period.periodDueDate(), period.principalDue(),
|
||||
period.interestDue(), BigDecimal.ZERO, BigDecimal.ZERO, false, compoundingDetails);
|
||||
|
||||
loan.addLoanRepaymentScheduleInstallment(repaymentScheduleInstallment);
|
||||
repaymentScheduleInstallments.add(repaymentScheduleInstallment) ;
|
||||
}
|
||||
|
||||
else {
|
||||
for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
|
||||
if (repaymentScheduleInstallment.getInstallmentNumber().equals(period.oldPeriodNumber())) {
|
||||
|
||||
LocalDate periodDueDate = repaymentScheduleInstallment.getDueDate();
|
||||
Money zeroAmount = Money.of(currency, new BigDecimal(0));
|
||||
|
||||
repaymentScheduleInstallment.updateInstallmentNumber(period.periodNumber());
|
||||
repaymentScheduleInstallment.updateFromDate(period.periodFromDate());
|
||||
repaymentScheduleInstallment.updateDueDate(period.periodDueDate());
|
||||
repaymentScheduleInstallment.updatePrincipal(period.principalDue());
|
||||
repaymentScheduleInstallment.updateInterestCharged(period.interestDue());
|
||||
|
||||
if (Money.of(currency, period.principalDue()).isZero() && Money.of(currency, period.interestDue()).isZero()
|
||||
&& repaymentScheduleInstallment.isNotFullyPaidOff()) {
|
||||
|
||||
if (repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isGreaterThan(zeroAmount)
|
||||
|| repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isGreaterThan(zeroAmount)) {
|
||||
|
||||
waiveLoanCharges.addAll(loan.getLoanCharges(periodDueDate));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
LocalDate rescheduleFromDate = null;
|
||||
Set<LoanTermVariations> activeLoanTermVariations = loan.getActiveLoanTermVariations();
|
||||
LoanTermVariations dueDateVariationInCurrentRequest = loanRescheduleRequest.getDueDateTermVariationIfExists();
|
||||
if (dueDateVariationInCurrentRequest != null && activeLoanTermVariations != null) {
|
||||
LocalDate fromScheduleDate = dueDateVariationInCurrentRequest.fetchTermApplicaDate();
|
||||
LocalDate currentScheduleDate = fromScheduleDate;
|
||||
LocalDate modifiedScheduleDate = dueDateVariationInCurrentRequest.fetchDateValue();
|
||||
Map<LocalDate, LocalDate> changeMap = new HashMap<>();
|
||||
changeMap.put(currentScheduleDate, modifiedScheduleDate);
|
||||
for (LoanTermVariations activeLoanTermVariation : activeLoanTermVariations) {
|
||||
if (activeLoanTermVariation.getTermType().isDueDateVariation()
|
||||
&& activeLoanTermVariation.fetchDateValue().equals(dueDateVariationInCurrentRequest.fetchTermApplicaDate())) {
|
||||
activeLoanTermVariation.markAsInactive();
|
||||
rescheduleFromDate = activeLoanTermVariation.fetchTermApplicaDate();
|
||||
dueDateVariationInCurrentRequest.setTermApplicableFrom(rescheduleFromDate.toDate());
|
||||
} else if (!activeLoanTermVariation.fetchTermApplicaDate().isBefore(fromScheduleDate)) {
|
||||
while (currentScheduleDate.isBefore(activeLoanTermVariation.fetchTermApplicaDate())) {
|
||||
currentScheduleDate = this.scheduledDateGenerator.generateNextRepaymentDate(currentScheduleDate,
|
||||
loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
modifiedScheduleDate = this.scheduledDateGenerator.generateNextRepaymentDate(modifiedScheduleDate,
|
||||
loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
changeMap.put(currentScheduleDate, modifiedScheduleDate);
|
||||
}
|
||||
if (changeMap.containsKey(activeLoanTermVariation.fetchTermApplicaDate())) {
|
||||
activeLoanTermVariation.setTermApplicableFrom(changeMap.get(activeLoanTermVariation.fetchTermApplicaDate())
|
||||
.toDate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
|
||||
this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
|
||||
}
|
||||
|
||||
loan.updateRescheduledByUser(appUser);
|
||||
loan.updateRescheduledOnDate(new LocalDate());
|
||||
|
||||
// waive all loan charges of zero instalments
|
||||
waiveLoanCharges(loan, waiveLoanCharges);
|
||||
|
||||
// update the Loan summary
|
||||
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true, null);
|
||||
|
||||
// update the total number of schedule repayments
|
||||
loan.updateNumberOfRepayments(periods.size());
|
||||
|
||||
// update the loan term frequency (loan term frequency = number
|
||||
// of repayments)
|
||||
loan.updateTermFrequency(periods.size());
|
||||
|
||||
// update the status of the request
|
||||
loanRescheduleRequest.approve(appUser, approvedOnDate);
|
||||
|
||||
// update the derived fields of each loan repayments schedule
|
||||
// instalments
|
||||
for (final LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
repaymentScheduleInstallment.updateDerivedFields(currency, new LocalDate());
|
||||
}
|
||||
|
||||
// updates maturity date
|
||||
loan.updateLoanScheduleDependentDerivedFields();
|
||||
// update the loan object
|
||||
this.loanRepository.save(loan);
|
||||
}
|
||||
if (rescheduleFromDate == null) {
|
||||
rescheduleFromDate = loanRescheduleRequest.getRescheduleFromDate();
|
||||
}
|
||||
for (LoanRescheduleRequestToTermVariationMapping mapping : loanRescheduleRequest
|
||||
.getLoanRescheduleRequestToTermVariationMappings()) {
|
||||
mapping.getLoanTermVariations().updateIsActive(true);
|
||||
}
|
||||
BigDecimal annualNominalInterestRate = null;
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
loan.constructLoanTermVariations(scheduleGeneratorDTO.getFloatingRateDTO(), annualNominalInterestRate, loanTermVariations);
|
||||
loanApplicationTerms.getLoanTermVariations().setExceptionData(loanTermVariations);
|
||||
|
||||
/*for (LoanTermVariationsData loanTermVariation : loanApplicationTerms.getLoanTermVariations().getDueDateVariation()) {
|
||||
if (rescheduleFromDate.isBefore(loanTermVariation.getTermApplicableFrom())) {
|
||||
LocalDate applicableDate = this.scheduledDateGenerator.generateNextRepaymentDate(rescheduleFromDate,
|
||||
loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
if (loanTermVariation.getTermApplicableFrom().equals(applicableDate)) {
|
||||
LocalDate adjustedDate = this.scheduledDateGenerator.generateNextRepaymentDate(adjustedApplicableDate,
|
||||
loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
loanTermVariation.setApplicableFromDate(adjustedDate);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
|
||||
loan.setHelpers(loanLifecycleStateMachine, this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms,
|
||||
loan, loanApplicationTerms.getHolidayDetailDTO(),
|
||||
loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
||||
|
||||
loan.updateLoanSchedule(loanSchedule.getInstallments(), appUser);
|
||||
loan.recalculateAllCharges();
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.processTransactions();
|
||||
|
||||
for (LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
|
||||
this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
|
||||
}
|
||||
|
||||
loan.updateRescheduledByUser(appUser);
|
||||
loan.updateRescheduledOnDate(new LocalDate());
|
||||
|
||||
// update the status of the request
|
||||
loanRescheduleRequest.approve(appUser, approvedOnDate);
|
||||
|
||||
// update the loan object
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||
// update loan with references to the newly created
|
||||
// transactions
|
||||
loan.addLoanTransaction(mapEntry.getValue());
|
||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
}
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.loanAccountDomainService.recalculateAccruals(loan, true);
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
|
||||
.withLoanId(loanRescheduleRequest.getLoan().getId()).with(changes).build();
|
||||
@@ -462,60 +500,24 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* waive all charges in the collection
|
||||
*
|
||||
* @param loan
|
||||
* Loan object
|
||||
* @param loanCharges
|
||||
* collection of LoanCharge objects
|
||||
* @return void
|
||||
**/
|
||||
private void waiveLoanCharges(Loan loan, Collection<LoanCharge> loanCharges) {
|
||||
AppUser currentUser = this.platformSecurityContext.authenticatedUser();
|
||||
this.loanAssembler.setHelpers(loan);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
|
||||
if (loanCharge.isChargePending()) {
|
||||
Integer loanInstallmentNumber = null;
|
||||
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
LoanInstallmentCharge chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
|
||||
|
||||
if (chargePerInstallment != null) {
|
||||
loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
|
||||
}
|
||||
private void saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
try {
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.getId() == null) {
|
||||
this.repaymentScheduleInstallmentRepository.save(installment);
|
||||
}
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<>(3);
|
||||
|
||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
LocalDate recalculateFrom = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
recalculateFrom = DateUtils.getLocalDateOfTenant();
|
||||
}
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
|
||||
Money accruedCharge = Money.zero(loan.getCurrency());
|
||||
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy(
|
||||
loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber);
|
||||
for (LoanChargePaidByData chargePaidByData : chargePaidByDatas) {
|
||||
accruedCharge = accruedCharge.plus(chargePaidByData.getAmount());
|
||||
}
|
||||
}
|
||||
|
||||
final LoanTransaction loanTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge,
|
||||
currentUser);
|
||||
|
||||
this.loanTransactionRepository.save(loanTransaction);
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
this.loanRepository.saveAndFlush(loan);
|
||||
} catch (final DataIntegrityViolationException e) {
|
||||
final Throwable realCause = e.getCause();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
if (realCause.getMessage().toLowerCase().contains("external_id_unique")) {
|
||||
baseDataValidator.reset().parameter("externalId").failWithCode("value.must.be.unique");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,11 +530,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
final List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
|
||||
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult reject(JsonCommand jsonCommand) {
|
||||
@@ -562,6 +559,11 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
loanRescheduleRequest.reject(appUser, rejectedOnDate);
|
||||
Set<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings = loanRescheduleRequest
|
||||
.getLoanRescheduleRequestToTermVariationMappings();
|
||||
for (LoanRescheduleRequestToTermVariationMapping loanRescheduleRequestToTermVariationMapping : loanRescheduleRequestToTermVariationMappings) {
|
||||
loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().markAsInactive();
|
||||
}
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
|
||||
@@ -593,20 +595,4 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = null;
|
||||
try {
|
||||
baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate().getRatePeriods();
|
||||
} catch (final FloatingRateNotFoundException ex) {
|
||||
// Do not do anything
|
||||
}
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -551,6 +551,22 @@ public class LoanEnumerations {
|
||||
optionData = new EnumOptionData(LoanTermVariationType.PRINCIPAL_AMOUNT.getValue().longValue(),
|
||||
LoanTermVariationType.PRINCIPAL_AMOUNT.getCode(), "principalAmount");
|
||||
break;
|
||||
case GRACE_ON_INTEREST:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.GRACE_ON_INTEREST.getValue().longValue(),
|
||||
LoanTermVariationType.GRACE_ON_INTEREST.getCode(), "graceOnInterest");
|
||||
break;
|
||||
case GRACE_ON_PRINCIPAL:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.GRACE_ON_PRINCIPAL.getValue().longValue(),
|
||||
LoanTermVariationType.GRACE_ON_PRINCIPAL.getCode(), "graceOnPrincipal");
|
||||
break;
|
||||
case EXTEND_REPAYMENT_PERIOD:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue().longValue(),
|
||||
LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getCode(), "extendRepaymentPeriod");
|
||||
break;
|
||||
case INTEREST_RATE_FROM_INSTALLMENT:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getValue().longValue(),
|
||||
LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getCode(), "interestRateForInstallment");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.INVALID.getValue().longValue(),
|
||||
LoanTermVariationType.INVALID.getCode(), "Invalid");
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
ALTER TABLE `m_loan_term_variations`
|
||||
ADD COLUMN `is_active` TINYINT(1) NOT NULL DEFAULT '1' AFTER `applied_on_loan_status`;
|
||||
|
||||
ALTER TABLE `m_loan_term_variations`
|
||||
ADD COLUMN `parent_id` BIGINT(20) NULL DEFAULT NULL AFTER `is_active`;
|
||||
|
||||
ALTER TABLE `m_loan_term_variations`
|
||||
ADD COLUMN `reshedule_request_id` BIGINT(20) NULL DEFAULT NULL AFTER `parent_id`;
|
||||
|
||||
insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
|
||||
(
|
||||
select
|
||||
mlrr.loan_id,
|
||||
7 term_type,
|
||||
ifnull(mlrr.adjusted_due_date,mlrr.reschedule_from_date) applicable_date,
|
||||
mlrr.grace_on_interest decimal_value,
|
||||
null date_value,
|
||||
1 is_specific_to_installment,
|
||||
300 applied_on_loan_status_id,
|
||||
mlrr.id reshedule_request_id,
|
||||
if(mlrr.status_enum = 200 ,1,0) is_active,
|
||||
null parent_id
|
||||
from m_loan_reschedule_request mlrr
|
||||
where mlrr.grace_on_interest is not null);
|
||||
|
||||
insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
|
||||
(
|
||||
select
|
||||
mlrr.loan_id,
|
||||
8 term_type,
|
||||
ifnull(mlrr.adjusted_due_date,mlrr.reschedule_from_date) applicable_date,
|
||||
mlrr.grace_on_principal decimal_value,
|
||||
null date_value,
|
||||
1 is_specific_to_installment,
|
||||
300 applied_on_loan_status_id,
|
||||
mlrr.id reshedule_request_id,
|
||||
if(mlrr.status_enum = 200 ,1,0) is_active,
|
||||
null parent_id
|
||||
from m_loan_reschedule_request mlrr
|
||||
where mlrr.grace_on_principal is not null);
|
||||
|
||||
insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
|
||||
(
|
||||
select
|
||||
mlrr.loan_id,
|
||||
4 term_type,
|
||||
mlrr.reschedule_from_date applicable_date,
|
||||
null decimal_value,
|
||||
mlrr.adjusted_due_date date_value,
|
||||
1 is_specific_to_installment,
|
||||
300 applied_on_loan_status_id,
|
||||
mlrr.id reshedule_request_id,
|
||||
if(mlrr.status_enum = 200 ,1,0) is_active,
|
||||
null parent_id
|
||||
from m_loan_reschedule_request mlrr
|
||||
where mlrr.adjusted_due_date is not null);
|
||||
|
||||
insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
|
||||
(
|
||||
select
|
||||
mlrr.loan_id,
|
||||
9 term_type,
|
||||
ifnull(mlrr.adjusted_due_date,mlrr.reschedule_from_date) applicable_date,
|
||||
mlrr.extra_terms decimal_value,
|
||||
null date_value,
|
||||
1 is_specific_to_installment,
|
||||
300 applied_on_loan_status_id,
|
||||
mlrr.id reshedule_request_id,
|
||||
if(mlrr.status_enum = 200 ,1,0) is_active,
|
||||
null parent_id
|
||||
from m_loan_reschedule_request mlrr
|
||||
where mlrr.extra_terms is not null);
|
||||
|
||||
insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
|
||||
(
|
||||
select
|
||||
mlrr.loan_id,
|
||||
2 term_type,
|
||||
ifnull(mlrr.adjusted_due_date,mlrr.reschedule_from_date) applicable_date,
|
||||
mlrr.interest_rate decimal_value,
|
||||
null date_value,
|
||||
1 is_specific_to_installment,
|
||||
300 applied_on_loan_status_id,
|
||||
mlrr.id reshedule_request_id,
|
||||
if(mlrr.status_enum = 200 ,1,0) is_active,
|
||||
null parent_id
|
||||
from m_loan_reschedule_request mlrr
|
||||
where mlrr.interest_rate is not null);
|
||||
|
||||
insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
|
||||
(
|
||||
select
|
||||
mlrr.loan_id,
|
||||
9 term_type,
|
||||
mlrr.reschedule_from_date applicable_date,
|
||||
if(ifnull(mlrr.grace_on_principal,0) > ifnull(mlrr.grace_on_interest,0),mlrr.grace_on_principal,mlrr.grace_on_interest) decimal_value,
|
||||
null date_value,
|
||||
1 is_specific_to_installment,
|
||||
300 applied_on_loan_status_id,
|
||||
mlrr.id reshedule_request_id,
|
||||
if(mlrr.status_enum = 200 ,1,0) is_active,
|
||||
(select distinct mlt.id
|
||||
from m_loan_term_variations mlt
|
||||
where mlt.reshedule_request_id is not null
|
||||
and (mlt.term_type =8 or mlt.term_type = 7)
|
||||
and mlt.reshedule_request_id = mlrr.id
|
||||
order by mlt.term_type desc limit 1) parent_id
|
||||
from m_loan_reschedule_request mlrr
|
||||
where (mlrr.grace_on_interest is not null or mlrr.grace_on_principal is not null));
|
||||
|
||||
ALTER TABLE `m_loan_reschedule_request`
|
||||
DROP COLUMN `grace_on_principal`,
|
||||
DROP COLUMN `grace_on_interest`,
|
||||
DROP COLUMN `extra_terms`,
|
||||
DROP COLUMN `interest_rate`,
|
||||
DROP COLUMN `adjusted_due_date`;
|
||||
|
||||
CREATE TABLE `m_loan_reschedule_request_term_variations_mapping` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`loan_reschedule_request_id` BIGINT(20) NOT NULL,
|
||||
`loan_term_variations_id` BIGINT(20) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `FK__m_loan_reschedule_request` (`loan_reschedule_request_id`),
|
||||
INDEX `FK__m_loan_term_variations` (`loan_term_variations_id`),
|
||||
CONSTRAINT `FK__m_loan_reschedule_request` FOREIGN KEY (`loan_reschedule_request_id`) REFERENCES `m_loan_reschedule_request` (`id`),
|
||||
CONSTRAINT `FK__m_loan_term_variations` FOREIGN KEY (`loan_term_variations_id`) REFERENCES `m_loan_term_variations` (`id`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=1
|
||||
;
|
||||
|
||||
insert ignore into m_loan_reschedule_request_term_variations_mapping (`loan_reschedule_request_id`, `loan_term_variations_id`)
|
||||
(
|
||||
select distinct mltv.reshedule_request_id,mltv.id
|
||||
from m_loan_term_variations mltv
|
||||
where reshedule_request_id is not null
|
||||
);
|
||||
|
||||
ALTER TABLE `m_loan_term_variations`
|
||||
DROP COLUMN `reshedule_request_id`;
|
||||
Reference in New Issue
Block a user