MIFOSX-2228 : added recalcualte from date flexibility for interest recalculation
This commit is contained in:
+63
-32
@@ -96,6 +96,7 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentDa
|
|||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentDateException;
|
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentDateException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
import org.mifosplatform.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||||
@@ -364,6 +365,10 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
@Column(name = "guarantee_amount_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "guarantee_amount_derived", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal guaranteeAmountDerived;
|
private BigDecimal guaranteeAmountDerived;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.DATE)
|
||||||
|
@Column(name = "interest_recalcualated_on")
|
||||||
|
private Date interestRecalculatedOn;
|
||||||
|
|
||||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
||||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||||
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||||
@@ -496,8 +501,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
private Set<LoanCharge> associateChargesWithThisLoan(final Set<LoanCharge> loanCharges) {
|
private Set<LoanCharge> associateChargesWithThisLoan(final Set<LoanCharge> loanCharges) {
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
loanCharge.update(this);
|
loanCharge.update(this);
|
||||||
if(loanCharge.getTrancheDisbursementCharge() != null) {
|
if (loanCharge.getTrancheDisbursementCharge() != null) {
|
||||||
addTrancheLoanCharge(loanCharge.getCharge()) ;
|
addTrancheLoanCharge(loanCharge.getCharge());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loanCharges;
|
return loanCharges;
|
||||||
@@ -569,7 +574,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.charges.add(loanCharge);
|
this.charges.add(loanCharge);
|
||||||
|
|
||||||
this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
|
|
||||||
// store Id's of existing loan transactions and existing reversed loan
|
// store Id's of existing loan transactions and existing reversed loan
|
||||||
@@ -906,9 +911,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
Money chargeComponent = amountWaived;
|
Money chargeComponent = amountWaived;
|
||||||
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||||
Money receivableCharge = Money.zero(getCurrency());
|
Money receivableCharge = Money.zero(getCurrency());
|
||||||
if(loanInstallmentNumber !=null){
|
if (loanInstallmentNumber != null) {
|
||||||
receivableCharge = accruedCharge.minus(loanCharge.getInstallmentLoanCharge(loanInstallmentNumber).getAmountPaid(getCurrency()));
|
receivableCharge = accruedCharge.minus(loanCharge.getInstallmentLoanCharge(loanInstallmentNumber).getAmountPaid(
|
||||||
}else{ receivableCharge = accruedCharge.minus(loanCharge.getAmountPaid(getCurrency()));}
|
getCurrency()));
|
||||||
|
} else {
|
||||||
|
receivableCharge = accruedCharge.minus(loanCharge.getAmountPaid(getCurrency()));
|
||||||
|
}
|
||||||
if (receivableCharge.isLessThanZero()) {
|
if (receivableCharge.isLessThanZero()) {
|
||||||
receivableCharge = amountWaived.zero();
|
receivableCharge = amountWaived.zero();
|
||||||
}
|
}
|
||||||
@@ -924,10 +932,11 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
feeChargesWaived = Money.zero(loanCurrency());
|
feeChargesWaived = Money.zero(loanCurrency());
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
|
LocalDate transactionDate = getDisbursementDate();
|
||||||
if (loanCharge.isSpecifiedDueDate()) {
|
if (loanCharge.isSpecifiedDueDate()) {
|
||||||
transactionDate = loanCharge.getDueLocalDate();
|
transactionDate = loanCharge.getDueLocalDate();
|
||||||
}
|
}
|
||||||
|
scheduleGeneratorDTO.setRecalculateFrom(transactionDate);
|
||||||
|
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
|
|
||||||
@@ -1107,6 +1116,17 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateLoanSchedule(final Collection<LoanRepaymentScheduleInstallment> installments, AppUser currentUser) {
|
||||||
|
this.repaymentScheduleInstallments.clear();
|
||||||
|
for (final LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
|
addRepaymentScheduleInstallment(installment);
|
||||||
|
}
|
||||||
|
updateLoanScheduleDependentDerivedFields();
|
||||||
|
updateLoanSummaryDerivedFields();
|
||||||
|
applyAccurals(currentUser);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method updates accrual derived fields on installments and reverse the
|
* method updates accrual derived fields on installments and reverse the
|
||||||
* unprocessed transactions
|
* unprocessed transactions
|
||||||
@@ -1197,8 +1217,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
updateLoanOutstandingBalaces();
|
updateLoanOutstandingBalaces();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateLoanSummarAndStatus(){
|
public void updateLoanSummarAndStatus() {
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
doPostLoanTransactionChecks(getLastUserTransactionDate(), loanLifecycleStateMachine);
|
doPostLoanTransactionChecks(getLastUserTransactionDate(), loanLifecycleStateMachine);
|
||||||
}
|
}
|
||||||
@@ -1485,9 +1505,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMultiDisburmentLoan() {
|
public boolean isMultiDisburmentLoan() {
|
||||||
return this.loanProduct.isMultiDisburseLoan() ;
|
return this.loanProduct.isMultiDisburseLoan();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update interest recalculation settings if product configuration changes
|
* Update interest recalculation settings if product configuration changes
|
||||||
*/
|
*/
|
||||||
@@ -1660,12 +1680,13 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
List<Long> loanChargeIds = fetchLoanChargeIds();
|
List<Long> loanChargeIds = fetchLoanChargeIds();
|
||||||
int chargeIdLength = loanChargeIds.size();
|
int chargeIdLength = loanChargeIds.size();
|
||||||
String chargeIds = null;
|
String chargeIds = null;
|
||||||
//From modify application page, if user removes all charges, we should get empty array.
|
// From modify application page, if user removes all charges, we should
|
||||||
//So we need to remove all charges applied for this loan
|
// get empty array.
|
||||||
|
// So we need to remove all charges applied for this loan
|
||||||
boolean removeAllChages = false;
|
boolean removeAllChages = false;
|
||||||
if (jsonCommand.parameterExists(LoanApiConstants.chargesParameterName)) {
|
if (jsonCommand.parameterExists(LoanApiConstants.chargesParameterName)) {
|
||||||
JsonArray chargesArray = jsonCommand.arrayOfParameterNamed(LoanApiConstants.chargesParameterName);
|
JsonArray chargesArray = jsonCommand.arrayOfParameterNamed(LoanApiConstants.chargesParameterName);
|
||||||
if (chargesArray.size() == 0) removeAllChages = true;
|
if (chargesArray.size() == 0) removeAllChages = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jsonCommand.parameterExists(LoanApiConstants.disbursementDataParameterName)) {
|
if (jsonCommand.parameterExists(LoanApiConstants.disbursementDataParameterName)) {
|
||||||
@@ -1754,13 +1775,13 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
this.disbursementDetails.add(disbursementDetails);
|
this.disbursementDetails.add(disbursementDetails);
|
||||||
for (LoanTrancheCharge trancheCharge : trancheCharges) {
|
for (LoanTrancheCharge trancheCharge : trancheCharges) {
|
||||||
Charge chargeDefinition = trancheCharge.getCharge();
|
Charge chargeDefinition = trancheCharge.getCharge();
|
||||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, null, null, null,
|
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, null, null, null, new LocalDate(
|
||||||
new LocalDate(expectedDisbursementDate), null, null);
|
expectedDisbursementDate), null, null);
|
||||||
loanCharge.update(this);
|
loanCharge.update(this);
|
||||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge,
|
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge,
|
||||||
disbursementDetails);
|
disbursementDetails);
|
||||||
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
|
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
|
||||||
addLoanCharge(loanCharge);
|
addLoanCharge(loanCharge);
|
||||||
}
|
}
|
||||||
actualChanges.put(LoanApiConstants.disbursementDataParameterName, expectedDisbursementDate + "-" + principal);
|
actualChanges.put(LoanApiConstants.disbursementDataParameterName, expectedDisbursementDate + "-" + principal);
|
||||||
actualChanges.put("recalculateLoanSchedule", true);
|
actualChanges.put("recalculateLoanSchedule", true);
|
||||||
@@ -4857,9 +4878,10 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO, final AppUser currentUser) {
|
public void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO, final AppUser currentUser) {
|
||||||
|
|
||||||
LocalDate lastTransactionDate = getLastUserTransactionDate();
|
LocalDate lastTransactionDate = getLastUserTransactionDate();
|
||||||
final LoanScheduleModel loanSchedule = getRecalculatedSchedule(generatorDTO);
|
final LoanScheduleDTO loanSchedule = getRecalculatedSchedule(generatorDTO);
|
||||||
if (loanSchedule == null) { return; }
|
if (loanSchedule == null) { return; }
|
||||||
updateLoanSchedule(loanSchedule, currentUser);
|
updateLoanSchedule(loanSchedule.getInstallments(), currentUser);
|
||||||
|
this.interestRecalculatedOn = DateUtils.getDateOfTenant();
|
||||||
LocalDate lastRepaymentDate = this.getLastRepaymentPeriodDueDate();
|
LocalDate lastRepaymentDate = this.getLastRepaymentPeriodDueDate();
|
||||||
Set<LoanCharge> charges = this.charges();
|
Set<LoanCharge> charges = this.charges();
|
||||||
for (LoanCharge loanCharge : charges) {
|
for (LoanCharge loanCharge : charges) {
|
||||||
@@ -4896,7 +4918,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanScheduleModel getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) {
|
private LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) {
|
||||||
|
|
||||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa || !isOpen()) { return null; }
|
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa || !isOpen()) { return null; }
|
||||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||||
@@ -4912,10 +4934,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
.determineProcessor(this.transactionProcessingStrategy);
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, charges(),
|
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, charges(), generatorDTO.getHolidayDetailDTO(),
|
||||||
generatorDTO.getHolidayDetailDTO(), retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
|
retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor,
|
||||||
loanRepaymentScheduleTransactionProcessor);
|
repaymentScheduleInstallments, generatorDTO.getRecalculateFrom());
|
||||||
return loanSchedule;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanApplicationTerms constructLoanApplicationTerms(final ApplicationCurrency applicationCurrency,
|
public LoanApplicationTerms constructLoanApplicationTerms(final ApplicationCurrency applicationCurrency,
|
||||||
@@ -5017,8 +5038,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
getApprovedPrincipal());
|
getApprovedPrincipal());
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
.determineProcessor(this.transactionProcessingStrategy);
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(), onDate,
|
installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, charges(),
|
||||||
loanApplicationTerms, mc, charges(), holidayDetailDTO, retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
|
holidayDetailDTO, retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
|
||||||
loanRepaymentScheduleTransactionProcessor);
|
loanRepaymentScheduleTransactionProcessor);
|
||||||
} else {
|
} else {
|
||||||
installment = this.getTotalOutstandingOnLoan();
|
installment = this.getTotalOutstandingOnLoan();
|
||||||
@@ -5053,6 +5074,16 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return accruedTill;
|
return accruedTill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalDate fetchInterestRecalculateFromDate() {
|
||||||
|
LocalDate interestRecalculatedOn = null;
|
||||||
|
if (this.interestRecalculatedOn != null) {
|
||||||
|
interestRecalculatedOn = getDisbursementDate();
|
||||||
|
} else {
|
||||||
|
interestRecalculatedOn = new LocalDate(this.interestRecalculatedOn);
|
||||||
|
}
|
||||||
|
return interestRecalculatedOn;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateLoanOutstandingBalaces() {
|
private void updateLoanOutstandingBalaces() {
|
||||||
Money outstanding = Money.zero(getCurrency());
|
Money outstanding = Money.zero(getCurrency());
|
||||||
List<LoanTransaction> loanTransactions = retreiveListOfTransactionsExcludeAccruals();
|
List<LoanTransaction> loanTransactions = retreiveListOfTransactionsExcludeAccruals();
|
||||||
@@ -5473,8 +5504,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addTrancheLoanCharge(Charge charge) {
|
public void addTrancheLoanCharge(Charge charge) {
|
||||||
if(!trancheCharges.contains(charge)) {
|
if (!trancheCharges.contains(charge)) {
|
||||||
trancheCharges.add(new LoanTrancheCharge(charge, this));
|
trancheCharges.add(new LoanTrancheCharge(charge, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -186,6 +186,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
|
|
||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
|
recalculateFrom = transactionDate;
|
||||||
}
|
}
|
||||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||||
allowTransactionsOnNonWorkingDay);
|
allowTransactionsOnNonWorkingDay);
|
||||||
|
|||||||
+1
@@ -319,6 +319,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
public void reverse() {
|
public void reverse() {
|
||||||
this.reversed = true;
|
this.reversed = true;
|
||||||
|
this.loanTransactionToRepaymentScheduleMappings.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetDerivedComponents() {
|
public void resetDerivedComponents() {
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.portfolio.loanaccount.loanschedule.data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer object to return the schedule after generation of schedule
|
||||||
|
*/
|
||||||
|
public class LoanScheduleDTO {
|
||||||
|
|
||||||
|
private final List<LoanRepaymentScheduleInstallment> installments;
|
||||||
|
private final LoanScheduleModel loanScheduleModel;
|
||||||
|
|
||||||
|
private LoanScheduleDTO(final List<LoanRepaymentScheduleInstallment> installments, final LoanScheduleModel loanScheduleModel) {
|
||||||
|
this.installments = installments;
|
||||||
|
this.loanScheduleModel = loanScheduleModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LoanScheduleDTO from(final List<LoanRepaymentScheduleInstallment> installments, final LoanScheduleModel loanScheduleModel){
|
||||||
|
return new LoanScheduleDTO(installments, loanScheduleModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<LoanRepaymentScheduleInstallment> getInstallments() {
|
||||||
|
return this.installments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanScheduleModel getLoanScheduleModel() {
|
||||||
|
return this.loanScheduleModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+203
@@ -0,0 +1,203 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.portfolio.loanaccount.loanschedule.data;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.RecalculationDetail;
|
||||||
|
|
||||||
|
public class LoanScheduleRecalculationDTO {
|
||||||
|
|
||||||
|
private final int periodNumber;
|
||||||
|
private final int instalmentNumber;
|
||||||
|
private final LocalDate periodStartDate;
|
||||||
|
private final LocalDate actualRepaymentDate;
|
||||||
|
private final Money totalCumulativePrincipal;
|
||||||
|
private final Money totalCumulativeInterest;
|
||||||
|
private final Money totalFeeChargesCharged;
|
||||||
|
private final Money totalPenaltyChargesCharged;
|
||||||
|
private final Money totalRepaymentExpected;
|
||||||
|
private final Money reducePrincipal;
|
||||||
|
private final Map<LocalDate, Money> principalPortionMap;
|
||||||
|
private final Map<LocalDate, Money> latePaymentMap;
|
||||||
|
private final TreeMap<LocalDate, Money> compoundingMap;
|
||||||
|
private final Map<LocalDate, Money> disburseDetailMap;
|
||||||
|
private final Money outstandingBalance;
|
||||||
|
private final Money outstandingBalanceAsPerRest;
|
||||||
|
private final List<LoanRepaymentScheduleInstallment> installments;
|
||||||
|
private final Collection<RecalculationDetail> recalculationDetails;
|
||||||
|
private final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor;
|
||||||
|
private final LocalDate scheduleTillDate;
|
||||||
|
private final boolean partialUpdate;
|
||||||
|
|
||||||
|
private LoanScheduleRecalculationDTO(final int periodNumber, final int instalmentNumber, LocalDate periodStartDate,
|
||||||
|
final LocalDate actualRepaymentDate, final Money totalCumulativePrincipal, final Money totalCumulativeInterest,
|
||||||
|
final Money totalFeeChargesCharged, final Money totalPenaltyChargesCharged, final Money totalRepaymentExpected,
|
||||||
|
final Money reducePrincipal, final Map<LocalDate, Money> principalPortionMap, final Map<LocalDate, Money> latePaymentMap,
|
||||||
|
final TreeMap<LocalDate, Money> compoundingMap, final Map<LocalDate, Money> disburseDetailMap, final Money outstandingBalance,
|
||||||
|
final Money outstandingBalanceAsPerRest, final List<LoanRepaymentScheduleInstallment> installments,
|
||||||
|
final Collection<RecalculationDetail> recalculationDetails,
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||||
|
final boolean partialUpdate) {
|
||||||
|
this.periodNumber = periodNumber;
|
||||||
|
this.instalmentNumber = instalmentNumber;
|
||||||
|
this.periodStartDate = periodStartDate;
|
||||||
|
this.actualRepaymentDate = actualRepaymentDate;
|
||||||
|
this.totalCumulativePrincipal = totalCumulativePrincipal;
|
||||||
|
this.totalCumulativeInterest = totalCumulativeInterest;
|
||||||
|
this.totalFeeChargesCharged = totalFeeChargesCharged;
|
||||||
|
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
|
||||||
|
this.totalRepaymentExpected = totalRepaymentExpected;
|
||||||
|
this.reducePrincipal = reducePrincipal;
|
||||||
|
this.principalPortionMap = principalPortionMap;
|
||||||
|
this.latePaymentMap = latePaymentMap;
|
||||||
|
this.compoundingMap = compoundingMap;
|
||||||
|
this.disburseDetailMap = disburseDetailMap;
|
||||||
|
this.outstandingBalance = outstandingBalance;
|
||||||
|
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
|
||||||
|
this.installments = installments;
|
||||||
|
this.recalculationDetails = recalculationDetails;
|
||||||
|
this.loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessor;
|
||||||
|
this.scheduleTillDate = scheduleTillDate;
|
||||||
|
this.partialUpdate = partialUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LoanScheduleRecalculationDTO createLoanScheduleDTOForPartialUpdate(final int periodNumber, final int instalmentNumber,
|
||||||
|
LocalDate periodStartDate, final LocalDate actualRepaymentDate, final Money totalCumulativePrincipal,
|
||||||
|
final Money totalCumulativeInterest, final Money totalFeeChargesCharged, final Money totalPenaltyChargesCharged,
|
||||||
|
final Money totalRepaymentExpected, final Money reducePrincipal, final Map<LocalDate, Money> principalPortionMap,
|
||||||
|
final Map<LocalDate, Money> latePaymentMap, final TreeMap<LocalDate, Money> compoundingMap,
|
||||||
|
final Map<LocalDate, Money> disburseDetailMap, final Money outstandingBalance, final Money outstandingBalanceAsPerRest,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> installments, final Collection<RecalculationDetail> recalculationDetails,
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate) {
|
||||||
|
final boolean partialUpdate = true;
|
||||||
|
return new LoanScheduleRecalculationDTO(periodNumber, instalmentNumber, periodStartDate, actualRepaymentDate,
|
||||||
|
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||||
|
totalRepaymentExpected, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, disburseDetailMap,
|
||||||
|
outstandingBalance, outstandingBalanceAsPerRest, installments, recalculationDetails,
|
||||||
|
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LoanScheduleRecalculationDTO createLoanScheduleDTOForCompleteUpdate(
|
||||||
|
final Collection<RecalculationDetail> recalculationDetails,
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate) {
|
||||||
|
final int periodNumber = 1;
|
||||||
|
final int instalmentNumber = 1;
|
||||||
|
final LocalDate periodStartDate = null;
|
||||||
|
final LocalDate actualRepaymentDate = null;
|
||||||
|
final Money totalCumulativePrincipal = null;
|
||||||
|
final Money totalCumulativeInterest = null;
|
||||||
|
final Money totalFeeChargesCharged = null;
|
||||||
|
final Money totalPenaltyChargesCharged = null;
|
||||||
|
final Money totalRepaymentExpected = null;
|
||||||
|
final Money reducePrincipal = null;
|
||||||
|
final Map<LocalDate, Money> principalPortionMap = null;
|
||||||
|
final Map<LocalDate, Money> latePaymentMap = null;
|
||||||
|
final TreeMap<LocalDate, Money> compoundingMap = null;
|
||||||
|
final Map<LocalDate, Money> disburseDetailMap = null;
|
||||||
|
final Money outstandingBalance = null;
|
||||||
|
final Money outstandingBalanceAsPerRest = null;
|
||||||
|
final List<LoanRepaymentScheduleInstallment> installments = null;
|
||||||
|
final boolean partialUpdate = false;
|
||||||
|
return new LoanScheduleRecalculationDTO(periodNumber, instalmentNumber, periodStartDate, actualRepaymentDate,
|
||||||
|
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||||
|
totalRepaymentExpected, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, disburseDetailMap,
|
||||||
|
outstandingBalance, outstandingBalanceAsPerRest, installments, recalculationDetails,
|
||||||
|
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPeriodNumber() {
|
||||||
|
return this.periodNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInstalmentNumber() {
|
||||||
|
return this.instalmentNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getActualRepaymentDate() {
|
||||||
|
return this.actualRepaymentDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getTotalCumulativePrincipal() {
|
||||||
|
return this.totalCumulativePrincipal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getTotalCumulativeInterest() {
|
||||||
|
return this.totalCumulativeInterest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getTotalFeeChargesCharged() {
|
||||||
|
return this.totalFeeChargesCharged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getTotalPenaltyChargesCharged() {
|
||||||
|
return this.totalPenaltyChargesCharged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getTotalRepaymentExpected() {
|
||||||
|
return this.totalRepaymentExpected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getReducePrincipal() {
|
||||||
|
return this.reducePrincipal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<LocalDate, Money> getPrincipalPortionMap() {
|
||||||
|
return this.principalPortionMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<LocalDate, Money> getLatePaymentMap() {
|
||||||
|
return this.latePaymentMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeMap<LocalDate, Money> getCompoundingMap() {
|
||||||
|
return this.compoundingMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<LocalDate, Money> getDisburseDetailMap() {
|
||||||
|
return this.disburseDetailMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getOutstandingBalance() {
|
||||||
|
return this.outstandingBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getOutstandingBalanceAsPerRest() {
|
||||||
|
return this.outstandingBalanceAsPerRest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<LoanRepaymentScheduleInstallment> getInstallments() {
|
||||||
|
return this.installments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<RecalculationDetail> getRecalculationDetails() {
|
||||||
|
return this.recalculationDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanRepaymentScheduleTransactionProcessor getLoanRepaymentScheduleTransactionProcessor() {
|
||||||
|
return this.loanRepaymentScheduleTransactionProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getScheduleTillDate() {
|
||||||
|
return this.scheduleTillDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPartialUpdate() {
|
||||||
|
return this.partialUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getPeriodStartDate() {
|
||||||
|
return this.periodStartDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+442
-43
@@ -7,7 +7,15 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import org.joda.time.Days;
|
import org.joda.time.Days;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
@@ -22,8 +30,14 @@ import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
|||||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.*;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleRecalculationDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||||
@@ -42,16 +56,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
@Override
|
@Override
|
||||||
public LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
public LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO) {
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
final Collection<RecalculationDetail> diffAmt = null;
|
final LoanScheduleRecalculationDTO loanScheduleRecalculationDTO = null;
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = null;
|
return generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, loanScheduleRecalculationDTO);
|
||||||
final LocalDate scheduleTillDate = null;
|
|
||||||
return generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, diffAmt, loanRepaymentScheduleTransactionProcessor,
|
|
||||||
scheduleTillDate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final Collection<RecalculationDetail> transactions,
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate) {
|
final LoanScheduleRecalculationDTO loanScheduleRecalculationDTO) {
|
||||||
|
|
||||||
final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency();
|
final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency();
|
||||||
// generate list of proposed schedule due dates
|
// generate list of proposed schedule due dates
|
||||||
@@ -63,16 +74,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
// setup variables for tracking important facts required for loan
|
// setup variables for tracking important facts required for loan
|
||||||
// schedule generation.
|
// schedule generation.
|
||||||
Money principalToBeScheduled;
|
|
||||||
|
|
||||||
if (loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) {
|
Money principalToBeScheduled = getPrincipalToBeScheduled(loanApplicationTerms);
|
||||||
principalToBeScheduled = loanApplicationTerms.getApprovedPrincipal();
|
|
||||||
} else {
|
|
||||||
principalToBeScheduled = loanApplicationTerms.getPrincipal();
|
|
||||||
}
|
|
||||||
final MonetaryCurrency currency = principalToBeScheduled.getCurrency();
|
final MonetaryCurrency currency = principalToBeScheduled.getCurrency();
|
||||||
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
|
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
|
||||||
|
|
||||||
|
final LocalDate scheduleTillDate = loanScheduleRecalculationDTO == null ? null : loanScheduleRecalculationDTO.getScheduleTillDate();
|
||||||
|
final Collection<RecalculationDetail> transactions = loanScheduleRecalculationDTO == null ? null : loanScheduleRecalculationDTO
|
||||||
|
.getRecalculationDetails();
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanScheduleRecalculationDTO == null ? null
|
||||||
|
: loanScheduleRecalculationDTO.getLoanRepaymentScheduleTransactionProcessor();
|
||||||
|
|
||||||
// variables for cumulative totals
|
// variables for cumulative totals
|
||||||
int loanTermInDays = Integer.valueOf(0);
|
int loanTermInDays = Integer.valueOf(0);
|
||||||
final BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
|
final BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
|
||||||
@@ -107,10 +119,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// Actual period Number plus interest only repayments
|
// Actual period Number plus interest only repayments
|
||||||
int instalmentNumber = 1;
|
int instalmentNumber = 1;
|
||||||
|
|
||||||
|
// actual outstanding balance for interest calculation
|
||||||
Money outstandingBalance = principalToBeScheduled;
|
Money outstandingBalance = principalToBeScheduled;
|
||||||
|
|
||||||
// Set fixed EMI Amount
|
// Set fixed EMI Amount
|
||||||
updateFixedInstallmentAmount(mc, loanApplicationTerms, actualRepaymentDate, periodNumber, outstandingBalance, holidayDetailDTO);
|
if (loanApplicationTerms.getFixedEmiAmount() == null) {
|
||||||
|
updateFixedInstallmentAmount(mc, loanApplicationTerms, actualRepaymentDate, periodNumber, outstandingBalance, holidayDetailDTO);
|
||||||
|
}
|
||||||
|
|
||||||
// disbursement map for tranche details(will added to outstanding
|
// disbursement map for tranche details(will added to outstanding
|
||||||
// balance as per the start date)
|
// balance as per the start date)
|
||||||
@@ -165,6 +180,34 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
holidayDetailDTO.getWorkingDays().setRepaymentReschedulingType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue());
|
holidayDetailDTO.getWorkingDays().setRepaymentReschedulingType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue());
|
||||||
extendTermForDailyRepayments = true;
|
extendTermForDailyRepayments = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this block is to start the schedule generation from specified date
|
||||||
|
if (loanScheduleRecalculationDTO != null && loanScheduleRecalculationDTO.isPartialUpdate()) {
|
||||||
|
periodNumber = loanScheduleRecalculationDTO.getPeriodNumber();
|
||||||
|
instalmentNumber = loanScheduleRecalculationDTO.getInstalmentNumber();
|
||||||
|
periodStartDate = loanScheduleRecalculationDTO.getPeriodStartDate();
|
||||||
|
periodStartDateApplicableForInterest = periodStartDate;
|
||||||
|
actualRepaymentDate = loanScheduleRecalculationDTO.getActualRepaymentDate();
|
||||||
|
totalCumulativePrincipal = loanScheduleRecalculationDTO.getTotalCumulativePrincipal();
|
||||||
|
totalCumulativeInterest = loanScheduleRecalculationDTO.getTotalCumulativeInterest();
|
||||||
|
totalFeeChargesCharged = loanScheduleRecalculationDTO.getTotalFeeChargesCharged().getAmount();
|
||||||
|
totalPenaltyChargesCharged = loanScheduleRecalculationDTO.getTotalPenaltyChargesCharged().getAmount();
|
||||||
|
totalRepaymentExpected = loanScheduleRecalculationDTO.getTotalRepaymentExpected().getAmount();
|
||||||
|
reducePrincipal = loanScheduleRecalculationDTO.getReducePrincipal();
|
||||||
|
principalPortionMap.clear();
|
||||||
|
principalPortionMap.putAll(loanScheduleRecalculationDTO.getPrincipalPortionMap());
|
||||||
|
latePaymentMap.clear();
|
||||||
|
latePaymentMap.putAll(loanScheduleRecalculationDTO.getLatePaymentMap());
|
||||||
|
compoundingMap.clear();
|
||||||
|
compoundingMap.putAll(loanScheduleRecalculationDTO.getCompoundingMap());
|
||||||
|
disburseDetailMap.clear();
|
||||||
|
disburseDetailMap.putAll(loanScheduleRecalculationDTO.getDisburseDetailMap());
|
||||||
|
outstandingBalance = loanScheduleRecalculationDTO.getOutstandingBalance();
|
||||||
|
outstandingBalanceAsPerRest = loanScheduleRecalculationDTO.getOutstandingBalanceAsPerRest();
|
||||||
|
installments.clear();
|
||||||
|
installments.addAll(loanScheduleRecalculationDTO.getInstallments());
|
||||||
|
}
|
||||||
|
|
||||||
while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) {
|
while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) {
|
||||||
|
|
||||||
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||||
@@ -451,9 +494,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
LocalDate tillDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms,
|
if (arrears.isGreaterThanZero() && applicableDate.isBefore(lastRestDate)) {
|
||||||
holidayDetailDTO);
|
|
||||||
if (arrears.isGreaterThanZero() && applicableDate.isBefore(tillDate)) {
|
|
||||||
updateLatePaidAmountsToPrincipalMap(principalPortionMap, detail.getTransaction(), latePaymentMap,
|
updateLatePaidAmountsToPrincipalMap(principalPortionMap, detail.getTransaction(), latePaymentMap,
|
||||||
compoundingMap, loanApplicationTerms, currency, holidayDetailDTO, lastRestDate);
|
compoundingMap, loanApplicationTerms, currency, holidayDetailDTO, lastRestDate);
|
||||||
compoundingDateVariations.put(periodStartDateApplicableForInterest, new TreeMap<>(compoundingMap));
|
compoundingDateVariations.put(periodStartDateApplicableForInterest, new TreeMap<>(compoundingMap));
|
||||||
@@ -519,8 +560,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
if (earlyPaidAmount.isGreaterThanZero()) {
|
if (earlyPaidAmount.isGreaterThanZero()) {
|
||||||
reducePrincipal = reducePrincipal.plus(earlyPaidAmount);
|
reducePrincipal = reducePrincipal.plus(earlyPaidAmount);
|
||||||
BigDecimal fixedEmiAmount = loanApplicationTerms.getFixedEmiAmount();
|
BigDecimal fixedEmiAmount = loanApplicationTerms.getFixedEmiAmount();
|
||||||
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal, totalCumulativePrincipal,
|
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal,
|
||||||
periodNumber + 1, mc, holidayDetailDTO);
|
totalCumulativePrincipal.plus(principalForThisPeriod).plus(earlyPaidAmount), periodNumber + 1, mc, holidayDetailDTO);
|
||||||
if (loanApplicationTerms.getAmortizationMethod().isEqualInstallment()
|
if (loanApplicationTerms.getAmortizationMethod().isEqualInstallment()
|
||||||
&& fixedEmiAmount.compareTo(loanApplicationTerms.getFixedEmiAmount()) != 0) {
|
&& fixedEmiAmount.compareTo(loanApplicationTerms.getFixedEmiAmount()) != 0) {
|
||||||
isEmiAmountChanged = true;
|
isEmiAmountChanged = true;
|
||||||
@@ -740,6 +781,20 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
totalPenaltyChargesCharged, totalRepaymentExpected, totalOutstanding);
|
totalPenaltyChargesCharged, totalRepaymentExpected, totalOutstanding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this method calculates the principal amount for generating the repayment
|
||||||
|
* schedule.
|
||||||
|
*/
|
||||||
|
private Money getPrincipalToBeScheduled(final LoanApplicationTerms loanApplicationTerms) {
|
||||||
|
Money principalToBeScheduled;
|
||||||
|
if (loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) {
|
||||||
|
principalToBeScheduled = loanApplicationTerms.getApprovedPrincipal();
|
||||||
|
} else {
|
||||||
|
principalToBeScheduled = loanApplicationTerms.getPrincipal();
|
||||||
|
}
|
||||||
|
return principalToBeScheduled;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean updateFixedInstallmentAmount(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
private boolean updateFixedInstallmentAmount(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
LocalDate actualRepaymentDate, int periodNumber, Money outstandingBalance, final HolidayDetailDTO holidayDetailDTO) {
|
LocalDate actualRepaymentDate, int periodNumber, Money outstandingBalance, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
boolean isAmountChanged = false;
|
boolean isAmountChanged = false;
|
||||||
@@ -943,14 +998,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
case REDUCE_EMI_AMOUNT:
|
case REDUCE_EMI_AMOUNT:
|
||||||
// in this case emi amount will be reduced but number of
|
// in this case emi amount will be reduced but number of
|
||||||
// installments won't change
|
// installments won't change
|
||||||
|
Money principal = getPrincipalToBeScheduled(loanApplicationTerms);
|
||||||
if (loanApplicationTerms.getActualFixedEmiAmount() == null) {
|
if (loanApplicationTerms.getActualFixedEmiAmount() == null) {
|
||||||
loanApplicationTerms.setFixedEmiAmount(null);
|
loanApplicationTerms.setFixedEmiAmount(null);
|
||||||
updateFixedInstallmentAmount(mc, loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(),
|
updateFixedInstallmentAmount(mc, loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(),
|
||||||
periodNumber, loanApplicationTerms.getPrincipal().minus(totalCumulativePrincipal), holidayDetailDTO);
|
periodNumber, principal.minus(totalCumulativePrincipal), holidayDetailDTO);
|
||||||
}
|
}
|
||||||
if (loanApplicationTerms.getAmortizationMethod().isEqualPrincipal()) {
|
if (loanApplicationTerms.getAmortizationMethod().isEqualPrincipal()) {
|
||||||
loanApplicationTerms.updateFixedPrincipalAmount(mc, periodNumber,
|
loanApplicationTerms.updateFixedPrincipalAmount(mc, periodNumber, principal.minus(totalCumulativePrincipal));
|
||||||
loanApplicationTerms.getPrincipal().minus(totalCumulativePrincipal));
|
|
||||||
}
|
}
|
||||||
reducePrincipal = reducePrincipal.zero();
|
reducePrincipal = reducePrincipal.zero();
|
||||||
break;
|
break;
|
||||||
@@ -1743,23 +1798,353 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method calls schedule regeneration by passing transactions one after
|
* Method preprocess the installments and transactions and sets the required
|
||||||
* another(this is done mainly to handle the scenario where interest or fee
|
* fields to generate the schedule
|
||||||
* of over due installment should be collected before collecting principal )
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public LoanScheduleModel rescheduleNextInstallments(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
public LoanScheduleDTO rescheduleNextInstallments(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom) {
|
||||||
|
|
||||||
|
// Loan transactions to process and find the variation on payments
|
||||||
Collection<RecalculationDetail> recalculationDetails = new ArrayList<>();
|
Collection<RecalculationDetail> recalculationDetails = new ArrayList<>();
|
||||||
for (LoanTransaction loanTransaction : transactions) {
|
for (LoanTransaction loanTransaction : transactions) {
|
||||||
recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(), LoanTransaction
|
recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(), LoanTransaction
|
||||||
.copyTransactionProperties(loanTransaction)));
|
.copyTransactionProperties(loanTransaction)));
|
||||||
}
|
}
|
||||||
|
// Fixed schedule End Date for generating schedule
|
||||||
final LocalDate scheduleTillDate = null;
|
final LocalDate scheduleTillDate = null;
|
||||||
return generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, recalculationDetails,
|
LoanScheduleRecalculationDTO loanScheduleRecalculationDTO = null;
|
||||||
loanRepaymentScheduleTransactionProcessor, scheduleTillDate);
|
Collection<LoanScheduleModelPeriod> periods = new ArrayList<>();
|
||||||
|
final List<LoanRepaymentScheduleInstallment> retainedInstallments = new ArrayList<>();
|
||||||
|
|
||||||
|
// this block is to retain the schedule installments prior to the
|
||||||
|
// provided date and creates late and early payment details for further
|
||||||
|
// calculations
|
||||||
|
if (rescheduleFrom != null) {
|
||||||
|
Money principalToBeScheduled = getPrincipalToBeScheduled(loanApplicationTerms);
|
||||||
|
// actual outstanding balance for interest calculation
|
||||||
|
Money outstandingBalance = principalToBeScheduled;
|
||||||
|
// total outstanding balance as per rest for interest calculation.
|
||||||
|
Money outstandingBalanceAsPerRest = outstandingBalance;
|
||||||
|
|
||||||
|
// this is required to update total fee amounts in the
|
||||||
|
// LoanScheduleModel
|
||||||
|
final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges);
|
||||||
|
periods = createNewLoanScheduleListWithDisbursementDetails(loanApplicationTerms.getNumberOfRepayments(), loanApplicationTerms,
|
||||||
|
chargesDueAtTimeOfDisbursement);
|
||||||
|
final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();
|
||||||
|
MonetaryCurrency currency = outstandingBalance.getCurrency();
|
||||||
|
final Map<LocalDate, Money> disburseDetailMap = new HashMap<>();
|
||||||
|
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||||
|
// fetches the first tranche amount and also updates other
|
||||||
|
// tranche
|
||||||
|
// details to map
|
||||||
|
BigDecimal disburseAmt = getDisbursementAmount(loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(),
|
||||||
|
periods, chargesDueAtTimeOfDisbursement, disburseDetailMap, true);
|
||||||
|
outstandingBalance = outstandingBalance.zero().plus(disburseAmt);
|
||||||
|
outstandingBalanceAsPerRest = outstandingBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// early payments will be added here and as per the selected
|
||||||
|
// strategy
|
||||||
|
// action will be performed on this value
|
||||||
|
Money reducePrincipal = outstandingBalanceAsPerRest.zero();
|
||||||
|
|
||||||
|
// principal changes will be added along with date(after applying
|
||||||
|
// rest)
|
||||||
|
// from when these amounts will effect the outstanding balance for
|
||||||
|
// interest calculation
|
||||||
|
final Map<LocalDate, Money> principalPortionMap = new HashMap<>();
|
||||||
|
// compounding(principal) amounts will be added along with
|
||||||
|
// date(after applying compounding frequency)
|
||||||
|
// from when these amounts will effect the outstanding balance for
|
||||||
|
// interest calculation
|
||||||
|
final Map<LocalDate, Money> latePaymentMap = new HashMap<>();
|
||||||
|
|
||||||
|
// compounding(interest/Fee) amounts will be added along with
|
||||||
|
// date(after applying compounding frequency)
|
||||||
|
// from when these amounts will effect the outstanding balance for
|
||||||
|
// interest calculation
|
||||||
|
final TreeMap<LocalDate, Money> compoundingMap = new TreeMap<>();
|
||||||
|
LocalDate currentDate = DateUtils.getLocalDateOfTenant();
|
||||||
|
LocalDate lastRestDate = currentDate;
|
||||||
|
if (loanApplicationTerms.getRestCalendarInstance() != null) {
|
||||||
|
lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO);
|
||||||
|
}
|
||||||
|
LocalDate actualRepaymentDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
|
boolean isFirstRepayment = true;
|
||||||
|
|
||||||
|
// cumulative fields
|
||||||
|
Money totalCumulativePrincipal = principalToBeScheduled.zero();
|
||||||
|
Money totalCumulativeInterest = principalToBeScheduled.zero();
|
||||||
|
Money totalFeeChargesCharged = principalToBeScheduled.zero().plus(chargesDueAtTimeOfDisbursement);
|
||||||
|
Money totalPenaltyChargesCharged = principalToBeScheduled.zero();
|
||||||
|
Money totalRepaymentExpected = principalToBeScheduled.zero();
|
||||||
|
|
||||||
|
// Actual period Number as per the schedule
|
||||||
|
int periodNumber = 1;
|
||||||
|
// Actual period Number plus interest only repayments
|
||||||
|
int instalmentNumber = 1;
|
||||||
|
LocalDate lastInstallmentDate = actualRepaymentDate;
|
||||||
|
LocalDate periodStartDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
|
// Set fixed Amortization Amounts(either EMI or Principal )
|
||||||
|
updateAmortization(mc, loanApplicationTerms, actualRepaymentDate, periodNumber, outstandingBalance, holidayDetailDTO);
|
||||||
|
|
||||||
|
// Block process the installment and creates the period if it falls
|
||||||
|
// before reschedule from date
|
||||||
|
// This will create the recalculation details by applying the
|
||||||
|
// transactions
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
|
||||||
|
for (Map.Entry<LocalDate, Money> disburseDetail : disburseDetailMap.entrySet()) {
|
||||||
|
if (disburseDetail.getKey().isAfter(installment.getFromDate())
|
||||||
|
&& !disburseDetail.getKey().isAfter(installment.getDueDate())) {
|
||||||
|
// creates and add disbursement detail to the repayments
|
||||||
|
// period
|
||||||
|
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement(
|
||||||
|
disburseDetail.getKey(), disburseDetail.getValue(), chargesDueAtTimeOfDisbursement);
|
||||||
|
periods.add(disbursementPeriod);
|
||||||
|
// updates actual outstanding balance with new
|
||||||
|
// disbursement detail
|
||||||
|
outstandingBalance = outstandingBalance.plus(disburseDetail.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this will generate the next schedule due date and allows to
|
||||||
|
// process the installment only if recalculate from date is
|
||||||
|
// greater than due date
|
||||||
|
if (installment.getDueDate().isAfter(lastInstallmentDate)) {
|
||||||
|
LocalDate previousRepaymentDate = actualRepaymentDate;
|
||||||
|
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||||
|
isFirstRepayment, holidayDetailDTO);
|
||||||
|
isFirstRepayment = false;
|
||||||
|
lastInstallmentDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||||
|
holidayDetailDTO);
|
||||||
|
if (!lastInstallmentDate.isBefore(rescheduleFrom)) {
|
||||||
|
actualRepaymentDate = previousRepaymentDate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
periodNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculation of basic fields to start the schedule generation
|
||||||
|
// from the middle
|
||||||
|
periodStartDate = installment.getDueDate();
|
||||||
|
installment.resetDerivedComponents();
|
||||||
|
newRepaymentScheduleInstallments.add(installment);
|
||||||
|
outstandingBalance = outstandingBalance.minus(installment.getPrincipal(currency));
|
||||||
|
final LoanScheduleModelPeriod loanScheduleModelPeriod = createLoanScheduleModelPeriod(installment, outstandingBalance);
|
||||||
|
periods.add(loanScheduleModelPeriod);
|
||||||
|
totalCumulativePrincipal = totalCumulativePrincipal.plus(installment.getPrincipal(currency));
|
||||||
|
totalCumulativeInterest = totalCumulativeInterest.plus(installment.getInterestCharged(currency));
|
||||||
|
totalFeeChargesCharged = totalFeeChargesCharged.plus(installment.getFeeChargesCharged(currency));
|
||||||
|
totalPenaltyChargesCharged = totalPenaltyChargesCharged.plus(installment.getPenaltyChargesCharged(currency));
|
||||||
|
instalmentNumber++;
|
||||||
|
|
||||||
|
// populates the collection with transactions till the due date
|
||||||
|
// of
|
||||||
|
// the period for interest recalculation enabled loans
|
||||||
|
Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod(loanApplicationTerms,
|
||||||
|
installment.getDueDate(), recalculationDetails);
|
||||||
|
|
||||||
|
// calculates the expected principal value for this repayment
|
||||||
|
// schedule
|
||||||
|
Money principalPortionCalculated = principalToBeScheduled.zero();
|
||||||
|
if (!installment.isRecalculatedInterestComponent()) {
|
||||||
|
principalPortionCalculated = calculateExpectedPrincipalPortion(installment.getInterestCharged(currency),
|
||||||
|
loanApplicationTerms);
|
||||||
|
}
|
||||||
|
|
||||||
|
// expected principal considering the previously paid excess
|
||||||
|
// amount
|
||||||
|
Money actualPrincipalPortion = principalPortionCalculated.minus(reducePrincipal);
|
||||||
|
if (actualPrincipalPortion.isLessThanZero()) {
|
||||||
|
actualPrincipalPortion = principalPortionCalculated.zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO,
|
||||||
|
loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency, principalPortionMap,
|
||||||
|
installment, applicableTransactions, actualPrincipalPortion);
|
||||||
|
|
||||||
|
// this block is to adjust the period number based on the actual
|
||||||
|
// schedule due date and installment due date
|
||||||
|
// recalculatedInterestComponent installment shouldn't be
|
||||||
|
// considered while calculating fixed EMI amounts
|
||||||
|
int period = periodNumber;
|
||||||
|
if (!lastInstallmentDate.isEqual(installment.getDueDate())) {
|
||||||
|
period--;
|
||||||
|
}
|
||||||
|
reducePrincipal = fetchEarlyPaidAmount(installment.getPrincipal(currency), principalPortionCalculated, reducePrincipal,
|
||||||
|
loanApplicationTerms, totalCumulativePrincipal, period, mc, holidayDetailDTO);
|
||||||
|
// Updates principal paid map with efective date for reducing
|
||||||
|
// the amount from outstanding balance(interest calculation)
|
||||||
|
LocalDate amountApplicableDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms,
|
||||||
|
holidayDetailDTO);
|
||||||
|
// updates map with the installment principal amount excluding
|
||||||
|
// unprocessed amount since this amount is already accounted.
|
||||||
|
updateMapWithAmount(principalPortionMap, installment.getPrincipal(currency).minus(unprocessed), amountApplicableDate);
|
||||||
|
// update outstanding balance for interest calculation
|
||||||
|
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, installment.getDueDate(),
|
||||||
|
outstandingBalanceAsPerRest, false);
|
||||||
|
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, installment.getDueDate(),
|
||||||
|
outstandingBalanceAsPerRest, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
totalRepaymentExpected = totalCumulativePrincipal.plus(totalCumulativeInterest).plus(totalFeeChargesCharged)
|
||||||
|
.plus(totalPenaltyChargesCharged);
|
||||||
|
|
||||||
|
// updates the map with over due amounts
|
||||||
|
updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, lastInstallmentDate,
|
||||||
|
newRepaymentScheduleInstallments, true, lastRestDate, compoundingMap);
|
||||||
|
|
||||||
|
// for partial schedule generation
|
||||||
|
if (!newRepaymentScheduleInstallments.isEmpty()) {
|
||||||
|
loanScheduleRecalculationDTO = LoanScheduleRecalculationDTO
|
||||||
|
.createLoanScheduleDTOForPartialUpdate(periodNumber, instalmentNumber, periodStartDate, actualRepaymentDate,
|
||||||
|
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||||
|
totalRepaymentExpected, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap,
|
||||||
|
disburseDetailMap, outstandingBalance, outstandingBalanceAsPerRest, newRepaymentScheduleInstallments,
|
||||||
|
recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate);
|
||||||
|
retainedInstallments.addAll(newRepaymentScheduleInstallments);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// for complete schedule generation
|
||||||
|
if (loanScheduleRecalculationDTO == null) {
|
||||||
|
loanScheduleRecalculationDTO = LoanScheduleRecalculationDTO.createLoanScheduleDTOForCompleteUpdate(recalculationDetails,
|
||||||
|
loanRepaymentScheduleTransactionProcessor, scheduleTillDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||||
|
loanScheduleRecalculationDTO);
|
||||||
|
for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) {
|
||||||
|
if (loanScheduleModelPeriod.isRepaymentPeriod()) {
|
||||||
|
// adding newly created repayment periods to installments
|
||||||
|
addLoanRepaymentScheduleInstallment(retainedInstallments, loanScheduleModelPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
periods.addAll(loanScheduleModel.getPeriods());
|
||||||
|
LoanScheduleModel loanScheduleModelwithPeriodChanges = LoanScheduleModel.withLoanScheduleModelPeriods(periods, loanScheduleModel);
|
||||||
|
return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method identifies the early paid amounts for a installment and update the
|
||||||
|
* principal map for further calculations
|
||||||
|
*/
|
||||||
|
private Money updateEarlyPaidAmountsToMap(final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments, MonetaryCurrency currency,
|
||||||
|
final Map<LocalDate, Money> principalPortionMap, LoanRepaymentScheduleInstallment installment,
|
||||||
|
Collection<RecalculationDetail> applicableTransactions, Money actualPrincipalPortion) {
|
||||||
|
Money unprocessed = Money.zero(currency);
|
||||||
|
for (RecalculationDetail detail : applicableTransactions) {
|
||||||
|
if (!detail.isProcessed()) {
|
||||||
|
Money principalProcessed = installment.getPrincipalCompleted(currency);
|
||||||
|
List<LoanTransaction> currentTransactions = new ArrayList<>(2);
|
||||||
|
currentTransactions.add(detail.getTransaction());
|
||||||
|
// applies the transaction as per transaction strategy
|
||||||
|
// on scheduled installments to identify the
|
||||||
|
// unprocessed(early payment ) amounts
|
||||||
|
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency,
|
||||||
|
newRepaymentScheduleInstallments);
|
||||||
|
|
||||||
|
// Identifies totalEarlyPayment and early paid amount with this
|
||||||
|
// transaction
|
||||||
|
Money principalPaidWithTransaction = installment.getPrincipalCompleted(currency).minus(principalProcessed);
|
||||||
|
Money totalEarlyPayment = installment.getPrincipalCompleted(currency).minus(actualPrincipalPortion);
|
||||||
|
|
||||||
|
if (totalEarlyPayment.isGreaterThanZero()) {
|
||||||
|
unprocessed = principalPaidWithTransaction;
|
||||||
|
// will execute this block if partial amount paid as
|
||||||
|
// early
|
||||||
|
if (principalPaidWithTransaction.isGreaterThan(totalEarlyPayment)) {
|
||||||
|
unprocessed = totalEarlyPayment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// updates principal portion map with the early
|
||||||
|
// payment amounts and applicable date as per rest
|
||||||
|
LocalDate applicableDate = getNextRestScheduleDate(detail.getTransactionDate().minusDays(1), loanApplicationTerms,
|
||||||
|
holidayDetailDTO);
|
||||||
|
updateMapWithAmount(principalPortionMap, unprocessed, applicableDate);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unprocessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAmortization(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, LocalDate actualRepaymentDate,
|
||||||
|
int periodNumber, Money outstandingBalance, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
|
if (loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
|
||||||
|
updateFixedInstallmentAmount(mc, loanApplicationTerms, actualRepaymentDate, periodNumber, outstandingBalance, holidayDetailDTO);
|
||||||
|
} else {
|
||||||
|
loanApplicationTerms.updateFixedPrincipalAmount(mc, periodNumber, outstandingBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method identifies early paid amount and applies the early payment
|
||||||
|
* strategy
|
||||||
|
*/
|
||||||
|
private Money fetchEarlyPaidAmount(final Money principalPortion, final Money principalPortionCalculated, final Money reducePrincipal,
|
||||||
|
final LoanApplicationTerms applicationTerms, final Money totalCumulativePrincipal, int periodNumber, final MathContext mc,
|
||||||
|
final HolidayDetailDTO holidayDetailDTO) {
|
||||||
|
Money existingEarlyPayment = reducePrincipal.minus(principalPortionCalculated);
|
||||||
|
Money earlyPaidAmount = principalPortion.plus(existingEarlyPayment);
|
||||||
|
if (existingEarlyPayment.isLessThanZero()) {
|
||||||
|
existingEarlyPayment = existingEarlyPayment.zero();
|
||||||
|
}
|
||||||
|
boolean isEarlyPaid = earlyPaidAmount.isGreaterThan(existingEarlyPayment);
|
||||||
|
|
||||||
|
if (earlyPaidAmount.isLessThanZero()) {
|
||||||
|
earlyPaidAmount = earlyPaidAmount.zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEarlyPaid) {
|
||||||
|
switch (applicationTerms.getRescheduleStrategyMethod()) {
|
||||||
|
case REDUCE_EMI_AMOUNT:
|
||||||
|
// in this case emi amount will be reduced but number of
|
||||||
|
// installments won't change
|
||||||
|
Money principal = getPrincipalToBeScheduled(applicationTerms);
|
||||||
|
if (applicationTerms.getActualFixedEmiAmount() == null) {
|
||||||
|
applicationTerms.setFixedEmiAmount(null);
|
||||||
|
updateFixedInstallmentAmount(mc, applicationTerms, applicationTerms.getExpectedDisbursementDate(), periodNumber,
|
||||||
|
principal.minus(totalCumulativePrincipal), holidayDetailDTO);
|
||||||
|
}
|
||||||
|
if (applicationTerms.getAmortizationMethod().isEqualPrincipal()) {
|
||||||
|
applicationTerms.updateFixedPrincipalAmount(mc, periodNumber, principal.minus(totalCumulativePrincipal));
|
||||||
|
}
|
||||||
|
earlyPaidAmount = earlyPaidAmount.zero();
|
||||||
|
break;
|
||||||
|
case REDUCE_NUMBER_OF_INSTALLMENTS:
|
||||||
|
// number of installments will reduce but emi amount won't
|
||||||
|
// get effected
|
||||||
|
earlyPaidAmount = earlyPaidAmount.zero();
|
||||||
|
break;
|
||||||
|
case RESCHEDULE_NEXT_REPAYMENTS:
|
||||||
|
// will reduce principal from the reduce Principal for each
|
||||||
|
// installment(means installments will have less emi amount)
|
||||||
|
// until this
|
||||||
|
// amount becomes zero
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return earlyPaidAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Money calculateExpectedPrincipalPortion(final Money interestPortion, final LoanApplicationTerms applicationTerms) {
|
||||||
|
Money principalPortionCalculated = interestPortion.zero();
|
||||||
|
if (applicationTerms.getAmortizationMethod().isEqualInstallment()) {
|
||||||
|
principalPortionCalculated = principalPortionCalculated.plus(applicationTerms.getFixedEmiAmount()).minus(interestPortion);
|
||||||
|
} else {
|
||||||
|
principalPortionCalculated = principalPortionCalculated.plus(applicationTerms.getFixedPrincipalAmount());
|
||||||
|
}
|
||||||
|
return principalPortionCalculated;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LoanRepaymentScheduleInstallment> fetchInstallmentsFromScheduleModel(final LoanScheduleModel loanScheduleModel) {
|
private List<LoanRepaymentScheduleInstallment> fetchInstallmentsFromScheduleModel(final LoanScheduleModel loanScheduleModel) {
|
||||||
@@ -1770,16 +2155,29 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
return installments;
|
return installments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLoanRepaymentScheduleInstallment(final List<LoanRepaymentScheduleInstallment> installments,
|
private LoanRepaymentScheduleInstallment addLoanRepaymentScheduleInstallment(final List<LoanRepaymentScheduleInstallment> installments,
|
||||||
final LoanScheduleModelPeriod scheduledLoanInstallment) {
|
final LoanScheduleModelPeriod scheduledLoanInstallment) {
|
||||||
|
LoanRepaymentScheduleInstallment installment = null;
|
||||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(null,
|
installment = new LoanRepaymentScheduleInstallment(null, scheduledLoanInstallment.periodNumber(),
|
||||||
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
scheduledLoanInstallment.periodFromDate(), scheduledLoanInstallment.periodDueDate(),
|
||||||
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
scheduledLoanInstallment.principalDue(), scheduledLoanInstallment.interestDue(),
|
||||||
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
scheduledLoanInstallment.feeChargesDue(), scheduledLoanInstallment.penaltyChargesDue(),
|
||||||
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent());
|
scheduledLoanInstallment.isRecalculatedInterestComponent());
|
||||||
installments.add(installment);
|
installments.add(installment);
|
||||||
}
|
}
|
||||||
|
return installment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanScheduleModelPeriod createLoanScheduleModelPeriod(final LoanRepaymentScheduleInstallment installment,
|
||||||
|
final Money outstandingPrincipal) {
|
||||||
|
final MonetaryCurrency currency = outstandingPrincipal.getCurrency();
|
||||||
|
LoanScheduleModelPeriod scheduledLoanInstallment = LoanScheduleModelRepaymentPeriod
|
||||||
|
.repayment(installment.getInstallmentNumber(), installment.getFromDate(), installment.getDueDate(),
|
||||||
|
installment.getPrincipal(currency), outstandingPrincipal, installment.getInterestCharged(currency),
|
||||||
|
installment.getFeeChargesCharged(currency), installment.getPenaltyChargesCharged(currency),
|
||||||
|
installment.getDue(currency), installment.isRecalculatedInterestComponent());
|
||||||
|
return scheduledLoanInstallment;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalDate getNextRestScheduleDate(LocalDate startDate, LoanApplicationTerms loanApplicationTerms,
|
private LocalDate getNextRestScheduleDate(LocalDate startDate, LoanApplicationTerms loanApplicationTerms,
|
||||||
@@ -1815,9 +2213,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
* Method returns the amount payable to close the loan account as of today.
|
* Method returns the amount payable to close the loan account as of today.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(final MonetaryCurrency currency, final LocalDate onDate,
|
||||||
MonetaryCurrency currency, final LocalDate onDate, LoanApplicationTerms loanApplicationTerms, MathContext mc,
|
final LoanApplicationTerms loanApplicationTerms, final MathContext mc, final Set<LoanCharge> charges,
|
||||||
Set<LoanCharge> charges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> loanTransactions,
|
final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> loanTransactions,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
||||||
|
|
||||||
LocalDate calculateTill = onDate;
|
LocalDate calculateTill = onDate;
|
||||||
@@ -1829,9 +2227,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(), LoanTransaction
|
recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(), LoanTransaction
|
||||||
.copyTransactionProperties(loanTransaction)));
|
.copyTransactionProperties(loanTransaction)));
|
||||||
}
|
}
|
||||||
LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, charges, holidayDetailDTO, recalculationDetails,
|
LoanScheduleRecalculationDTO loanScheduleRecalculationDTO = LoanScheduleRecalculationDTO.createLoanScheduleDTOForCompleteUpdate(
|
||||||
loanRepaymentScheduleTransactionProcessor, calculateTill);
|
recalculationDetails, loanRepaymentScheduleTransactionProcessor, calculateTill);
|
||||||
installments = fetchInstallmentsFromScheduleModel(loanScheduleModel);
|
LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, charges, holidayDetailDTO, loanScheduleRecalculationDTO);
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments = fetchInstallmentsFromScheduleModel(loanScheduleModel);
|
||||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions,
|
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions,
|
||||||
currency, installments, charges);
|
currency, installments, charges);
|
||||||
Money feeCharges = Money.zero(currency);
|
Money feeCharges = Money.zero(currency);
|
||||||
|
|||||||
+5
@@ -1270,4 +1270,9 @@ public final class LoanApplicationTerms {
|
|||||||
return loanCalendar;
|
return loanCalendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BigDecimal getFixedPrincipalAmount() {
|
||||||
|
return this.fixedPrincipalAmount;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+6
-6
@@ -19,6 +19,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||||
|
|
||||||
@@ -27,14 +28,13 @@ public interface LoanScheduleGenerator {
|
|||||||
LoanScheduleModel generate(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
LoanScheduleModel generate(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
||||||
final HolidayDetailDTO holidayDetailDTO);
|
final HolidayDetailDTO holidayDetailDTO);
|
||||||
|
|
||||||
LoanScheduleModel rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
LoanScheduleDTO rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
||||||
final HolidayDetailDTO holidayDetailDTO, List<LoanTransaction> transactions,
|
final HolidayDetailDTO holidayDetailDTO, List<LoanTransaction> transactions,
|
||||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor);
|
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, LocalDate rescheduleFrom);
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(MonetaryCurrency currency,
|
||||||
MonetaryCurrency currency, LocalDate onDate, LoanApplicationTerms loanApplicationTerms, MathContext mc,
|
LocalDate onDate, LoanApplicationTerms loanApplicationTerms, MathContext mc, Set<LoanCharge> charges,
|
||||||
Set<LoanCharge> charges, HolidayDetailDTO holidayDetailDTO, List<LoanTransaction> loanTransactions,
|
HolidayDetailDTO holidayDetailDTO, List<LoanTransaction> loanTransactions, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor);
|
||||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor);
|
|
||||||
|
|
||||||
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||||
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance,
|
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance,
|
||||||
|
|||||||
+9
-1
@@ -51,6 +51,15 @@ public final class LoanScheduleModel {
|
|||||||
loanScheduleModel.totalRepaymentExpected, loanScheduleModel.totalOutstanding);
|
loanScheduleModel.totalRepaymentExpected, loanScheduleModel.totalOutstanding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LoanScheduleModel withLoanScheduleModelPeriods(final Collection<LoanScheduleModelPeriod> periods,
|
||||||
|
final LoanScheduleModel loanScheduleModel) {
|
||||||
|
|
||||||
|
return new LoanScheduleModel(periods, loanScheduleModel.applicationCurrency, loanScheduleModel.loanTermInDays,
|
||||||
|
loanScheduleModel.totalPrincipalDisbursed, loanScheduleModel.totalPrincipalExpected, loanScheduleModel.totalPrincipalPaid,
|
||||||
|
loanScheduleModel.totalInterestCharged, loanScheduleModel.totalFeeChargesCharged,
|
||||||
|
loanScheduleModel.totalPenaltyChargesCharged, loanScheduleModel.totalRepaymentExpected, loanScheduleModel.totalOutstanding);
|
||||||
|
}
|
||||||
|
|
||||||
private LoanScheduleModel(final Collection<LoanScheduleModelPeriod> periods, final ApplicationCurrency applicationCurrency,
|
private LoanScheduleModel(final Collection<LoanScheduleModelPeriod> periods, final ApplicationCurrency applicationCurrency,
|
||||||
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
||||||
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
||||||
@@ -95,7 +104,6 @@ public final class LoanScheduleModel {
|
|||||||
return this.periods;
|
return this.periods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public BigDecimal getTotalPenaltyChargesCharged() {
|
public BigDecimal getTotalPenaltyChargesCharged() {
|
||||||
return this.totalPenaltyChargesCharged;
|
return this.totalPenaltyChargesCharged;
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-39
@@ -231,8 +231,8 @@ public class LoanScheduleAssembler {
|
|||||||
final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
|
final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
|
||||||
final LocalDate interestChargedFromDate = this.fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element);
|
final LocalDate interestChargedFromDate = this.fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element);
|
||||||
|
|
||||||
final Integer graceOnArrearsAgeing = this.fromApiJsonHelper
|
final Integer graceOnArrearsAgeing = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
|
||||||
.extractIntegerWithLocaleNamed(LoanProductConstants.graceOnArrearsAgeingParameterName, element);
|
LoanProductConstants.graceOnArrearsAgeingParameterName, element);
|
||||||
|
|
||||||
// other
|
// other
|
||||||
final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
|
final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
|
||||||
@@ -240,14 +240,14 @@ public class LoanScheduleAssembler {
|
|||||||
|
|
||||||
final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
||||||
element);
|
element);
|
||||||
final BigDecimal maxOutstandingBalance = this.fromApiJsonHelper
|
final BigDecimal maxOutstandingBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.maxOutstandingBalanceParameterName, element);
|
LoanApiConstants.maxOutstandingBalanceParameterName, element);
|
||||||
|
|
||||||
final List<DisbursementData> disbursementDatas = fetchDisbursementData(element.getAsJsonObject());
|
final List<DisbursementData> disbursementDatas = fetchDisbursementData(element.getAsJsonObject());
|
||||||
|
|
||||||
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<>();
|
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<>();
|
||||||
LoanTermVariationsData data = new LoanTermVariationsData(null, LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT),
|
LoanTermVariationsData data = new LoanTermVariationsData(null,
|
||||||
expectedDisbursementDate, emiAmount);
|
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount);
|
||||||
loanVariationTermsData.add(data);
|
loanVariationTermsData.add(data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -267,8 +267,8 @@ public class LoanScheduleAssembler {
|
|||||||
.getProductInterestRecalculationDetails();
|
.getProductInterestRecalculationDetails();
|
||||||
recalculationFrequencyType = loanProductInterestRecalculationDetails.getRestFrequencyType();
|
recalculationFrequencyType = loanProductInterestRecalculationDetails.getRestFrequencyType();
|
||||||
if (!recalculationFrequencyType.isSameAsRepayment()) {
|
if (!recalculationFrequencyType.isSameAsRepayment()) {
|
||||||
LocalDate calendarStartDate = this.fromApiJsonHelper
|
LocalDate calendarStartDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||||
.extractLocalDateNamed(LoanProductConstants.recalculationRestFrequencyDateParamName, element);
|
LoanProductConstants.recalculationRestFrequencyDateParamName, element);
|
||||||
if (calendarStartDate == null) {
|
if (calendarStartDate == null) {
|
||||||
calendarStartDate = expectedDisbursementDate;
|
calendarStartDate = expectedDisbursementDate;
|
||||||
}
|
}
|
||||||
@@ -280,8 +280,8 @@ public class LoanScheduleAssembler {
|
|||||||
if (compoundingMethod.isCompoundingEnabled()) {
|
if (compoundingMethod.isCompoundingEnabled()) {
|
||||||
compoundingFrequencyType = loanProductInterestRecalculationDetails.getCompoundingFrequencyType();
|
compoundingFrequencyType = loanProductInterestRecalculationDetails.getCompoundingFrequencyType();
|
||||||
if (!compoundingFrequencyType.isSameAsRepayment()) {
|
if (!compoundingFrequencyType.isSameAsRepayment()) {
|
||||||
LocalDate calendarStartDate = this.fromApiJsonHelper
|
LocalDate calendarStartDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||||
.extractLocalDateNamed(LoanProductConstants.recalculationCompoundingFrequencyDateParamName, element);
|
LoanProductConstants.recalculationCompoundingFrequencyDateParamName, element);
|
||||||
if (calendarStartDate == null) {
|
if (calendarStartDate == null) {
|
||||||
calendarStartDate = expectedDisbursementDate;
|
calendarStartDate = expectedDisbursementDate;
|
||||||
}
|
}
|
||||||
@@ -346,12 +346,12 @@ public class LoanScheduleAssembler {
|
|||||||
BigDecimal principal = null;
|
BigDecimal principal = null;
|
||||||
|
|
||||||
if (jsonObject.has(LoanApiConstants.disbursementDateParameterName)) {
|
if (jsonObject.has(LoanApiConstants.disbursementDateParameterName)) {
|
||||||
expectedDisbursementDate = this.fromApiJsonHelper
|
expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||||
.extractLocalDateNamed(LoanApiConstants.disbursementDateParameterName, jsonObject, dateFormat, locale);
|
LoanApiConstants.disbursementDateParameterName, jsonObject, dateFormat, locale);
|
||||||
}
|
}
|
||||||
if (jsonObject.has(LoanApiConstants.disbursementPrincipalParameterName)
|
if (jsonObject.has(LoanApiConstants.disbursementPrincipalParameterName)
|
||||||
&& jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive() && StringUtils
|
&& jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive()
|
||||||
.isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) {
|
&& StringUtils.isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) {
|
||||||
principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal();
|
principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal();
|
||||||
}
|
}
|
||||||
disbursementDatas.add(new DisbursementData(null, expectedDisbursementDate, null, principal, null, null));
|
disbursementDatas.add(new DisbursementData(null, expectedDisbursementDate, null, principal, null, null));
|
||||||
@@ -363,8 +363,9 @@ public class LoanScheduleAssembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void validateRepaymentsStartDateWithMeetingDates(final LocalDate repaymentsStartingFromDate, final Calendar calendar) {
|
private void validateRepaymentsStartDateWithMeetingDates(final LocalDate repaymentsStartingFromDate, final Calendar calendar) {
|
||||||
if (repaymentsStartingFromDate != null && !CalendarUtils.isValidRedurringDate(calendar.getRecurrence(),
|
if (repaymentsStartingFromDate != null
|
||||||
calendar.getStartDateLocalDate(), repaymentsStartingFromDate)) {
|
&& !CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(),
|
||||||
|
repaymentsStartingFromDate)) {
|
||||||
final String errorMessage = "First repayment date '" + repaymentsStartingFromDate + "' do not fall on a meeting date";
|
final String errorMessage = "First repayment date '" + repaymentsStartingFromDate + "' do not fall on a meeting date";
|
||||||
throw new LoanApplicationDateException("first.repayment.date.do.not.match.meeting.date", errorMessage,
|
throw new LoanApplicationDateException("first.repayment.date.do.not.match.meeting.date", errorMessage,
|
||||||
repaymentsStartingFromDate);
|
repaymentsStartingFromDate);
|
||||||
@@ -439,9 +440,10 @@ public class LoanScheduleAssembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LoanScheduleModel assembleLoanScheduleFrom(final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled,
|
public LoanScheduleModel assembleLoanScheduleFrom(final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays, final JsonElement element, Set<LoanDisbursementDetails> disbursementDetails) {
|
final List<Holiday> holidays, final WorkingDays workingDays, final JsonElement element,
|
||||||
|
Set<LoanDisbursementDetails> disbursementDetails) {
|
||||||
|
|
||||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element,disbursementDetails);
|
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element, disbursementDetails);
|
||||||
|
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||||
|
|
||||||
@@ -455,37 +457,38 @@ public class LoanScheduleAssembler {
|
|||||||
|
|
||||||
public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId,
|
public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId,
|
||||||
List<LoanTransaction> transactions, final Set<LoanCharge> loanCharges,
|
List<LoanTransaction> transactions, final Set<LoanCharge> loanCharges,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom) {
|
||||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
|
|
||||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId,
|
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, loanApplicationTerms
|
||||||
loanApplicationTerms.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
|
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||||
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, loanCharges, detailDTO, transactions,
|
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, loanCharges, detailDTO, transactions,
|
||||||
loanRepaymentScheduleTransactionProcessor);
|
loanRepaymentScheduleTransactionProcessor, repaymentScheduleInstallments, rescheduleFrom).getLoanScheduleModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(MonetaryCurrency currency, LocalDate onDate,
|
||||||
MonetaryCurrency currency, LocalDate onDate, LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges,
|
LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final Long officeId,
|
||||||
final Long officeId, List<LoanTransaction> loanTransactions,
|
List<LoanTransaction> loanTransactions,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
|
|
||||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId,
|
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, loanApplicationTerms
|
||||||
loanApplicationTerms.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
|
||||||
return loanScheduleGenerator.calculatePrepaymentAmount(installments, currency, onDate, loanApplicationTerms, mc, loanCharges,
|
return loanScheduleGenerator.calculatePrepaymentAmount(currency, onDate, loanApplicationTerms, mc, loanCharges, holidayDetailDTO,
|
||||||
holidayDetailDTO, loanTransactions, loanRepaymentScheduleTransactionProcessor);
|
loanTransactions, loanRepaymentScheduleTransactionProcessor);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +525,7 @@ public class LoanScheduleAssembler {
|
|||||||
refernceDateForCalculatingFirstRepaymentDate, repaymentPeriodFrequencyType,
|
refernceDateForCalculatingFirstRepaymentDate, repaymentPeriodFrequencyType,
|
||||||
minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
|
minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
|
||||||
|
|
||||||
} /*** Individual or group account, or JLG not linked to a meeting ***/
|
}/*** Individual or group account, or JLG not linked to a meeting ***/
|
||||||
else {
|
else {
|
||||||
LocalDate dateBasedOnRepaymentFrequency;
|
LocalDate dateBasedOnRepaymentFrequency;
|
||||||
// Derive the first repayment date as greater date among
|
// Derive the first repayment date as greater date among
|
||||||
@@ -535,12 +538,12 @@ public class LoanScheduleAssembler {
|
|||||||
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusWeeks(repaymentEvery);
|
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusWeeks(repaymentEvery);
|
||||||
} else if (repaymentPeriodFrequencyType.isMonthly()) {
|
} else if (repaymentPeriodFrequencyType.isMonthly()) {
|
||||||
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusMonths(repaymentEvery);
|
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusMonths(repaymentEvery);
|
||||||
} /** yearly loan **/
|
}/** yearly loan **/
|
||||||
else {
|
else {
|
||||||
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusYears(repaymentEvery);
|
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusYears(repaymentEvery);
|
||||||
}
|
}
|
||||||
derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment)
|
derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment) ? dateBasedOnRepaymentFrequency
|
||||||
? dateBasedOnRepaymentFrequency : dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment;
|
: dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
return derivedFirstRepayment;
|
return derivedFirstRepayment;
|
||||||
@@ -554,17 +557,16 @@ public class LoanScheduleAssembler {
|
|||||||
final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar,
|
final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar,
|
||||||
refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, frequency);
|
refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, frequency);
|
||||||
final LocalDate minimumFirstRepaymentDate = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
|
final LocalDate minimumFirstRepaymentDate = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment
|
return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment : deriveFirstRepaymentDateForJLGLoans(
|
||||||
: deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate, derivedFirstRepayment,
|
repaymentEvery, expectedDisbursementDate, derivedFirstRepayment, repaymentPeriodFrequencyType,
|
||||||
repaymentPeriodFrequencyType, minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
|
minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateMinimumDaysBetweenDisbursalAndFirstRepayment(final LocalDate disbursalDate, final LocalDate firstRepaymentDate,
|
private void validateMinimumDaysBetweenDisbursalAndFirstRepayment(final LocalDate disbursalDate, final LocalDate firstRepaymentDate,
|
||||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment) {
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment) {
|
||||||
|
|
||||||
final LocalDate minimumFirstRepaymentDate = disbursalDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
|
final LocalDate minimumFirstRepaymentDate = disbursalDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
if (firstRepaymentDate
|
if (firstRepaymentDate.isBefore(minimumFirstRepaymentDate)) { throw new MinDaysBetweenDisbursalAndFirstRepaymentViolationException(
|
||||||
.isBefore(minimumFirstRepaymentDate)) { throw new MinDaysBetweenDisbursalAndFirstRepaymentViolationException(disbursalDate,
|
disbursalDate, firstRepaymentDate, minimumDaysBetweenDisbursalAndFirstRepayment); }
|
||||||
firstRepaymentDate, minimumDaysBetweenDisbursalAndFirstRepayment); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-3
@@ -167,8 +167,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency,
|
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency,
|
||||||
calculatedRepaymentsStartingFromDate, restCalendarInstance, compoundingCalendarInstance);
|
calculatedRepaymentsStartingFromDate, restCalendarInstance, compoundingCalendarInstance);
|
||||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(
|
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(
|
||||||
loan.fetchRepaymentScheduleInstallments(), currency, today, loanApplicationTerms, loan.charges(), loan.getOfficeId(),
|
currency, today, loanApplicationTerms, loan.charges(), loan.getOfficeId(), loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
|
||||||
loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor);
|
loanRepaymentScheduleTransactionProcessor);
|
||||||
Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus(
|
Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus(
|
||||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||||
Money interestDue = Money.zero(currency);
|
Money interestDue = Money.zero(currency);
|
||||||
@@ -189,7 +189,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoanScheduleModel model = this.loanScheduleAssembler.assembleForInterestRecalculation(loanApplicationTerms, loan.getOfficeId(),
|
LoanScheduleModel model = this.loanScheduleAssembler.assembleForInterestRecalculation(loanApplicationTerms, loan.getOfficeId(),
|
||||||
modifiedTransactions, loan.charges(), loanRepaymentScheduleTransactionProcessor);
|
modifiedTransactions, loan.charges(), loanRepaymentScheduleTransactionProcessor, loan.fetchRepaymentScheduleInstallments(),
|
||||||
|
loan.fetchInterestRecalculateFromDate());
|
||||||
LoanScheduleData scheduleDate = model.toData();
|
LoanScheduleData scheduleDate = model.toData();
|
||||||
Collection<LoanSchedulePeriodData> periodDatas = scheduleDate.getPeriods();
|
Collection<LoanSchedulePeriodData> periodDatas = scheduleDate.getPeriods();
|
||||||
for (LoanSchedulePeriodData periodData : periodDatas) {
|
for (LoanSchedulePeriodData periodData : periodDatas) {
|
||||||
|
|||||||
+2
@@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
|||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||||
import org.mifosplatform.organisation.holiday.domain.HolidayRepositoryWrapper;
|
import org.mifosplatform.organisation.holiday.domain.HolidayRepositoryWrapper;
|
||||||
@@ -463,6 +464,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
|||||||
loan.getDisbursementDate().toDate());
|
loan.getDisbursementDate().toDate());
|
||||||
workingDays = this.workingDaysRepository.findOne();
|
workingDays = this.workingDaysRepository.findOne();
|
||||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
|
recalculateFrom = DateUtils.getLocalDateOfTenant();
|
||||||
}
|
}
|
||||||
|
|
||||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
|||||||
+1
-3
@@ -1071,9 +1071,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
if (installment.getId() == null) {
|
if (installment.getId() == null) {
|
||||||
this.repaymentScheduleInstallmentRepository.save(installment);
|
this.repaymentScheduleInstallmentRepository.save(installment);
|
||||||
} else {
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.loanRepository.saveAndFlush(loan);
|
this.loanRepository.saveAndFlush(loan);
|
||||||
} catch (final DataIntegrityViolationException e) {
|
} catch (final DataIntegrityViolationException e) {
|
||||||
|
|||||||
+3
-1
@@ -1695,14 +1695,16 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
|
sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
|
||||||
sqlBuilder.append(" and ml.interest_recalculation_enabled = 1 ");
|
sqlBuilder.append(" and ml.interest_recalculation_enabled = 1 ");
|
||||||
sqlBuilder.append(" and ml.is_npa = 0 ");
|
sqlBuilder.append(" and ml.is_npa = 0 ");
|
||||||
|
sqlBuilder.append(" and (ml.interest_recalcualated_on is null or ml.interest_recalcualated_on <> ?)");
|
||||||
sqlBuilder.append(" and ((");
|
sqlBuilder.append(" and ((");
|
||||||
sqlBuilder.append(" mr.completed_derived is false ");
|
sqlBuilder.append(" mr.completed_derived is false ");
|
||||||
sqlBuilder.append(" and mr.duedate < ? )");
|
sqlBuilder.append(" and mr.duedate < ? )");
|
||||||
sqlBuilder.append(" or dd.expected_disburse_date < ? ) ");
|
sqlBuilder.append(" or dd.expected_disburse_date < ? ) ");
|
||||||
sqlBuilder.append(" group by ml.id");
|
sqlBuilder.append(" group by ml.id");
|
||||||
try {
|
try {
|
||||||
|
String currentdate = formatter.print(DateUtils.getLocalDateOfTenant());
|
||||||
return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class,
|
return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class,
|
||||||
new Object[] { LoanStatus.ACTIVE.getValue(), formatter.print(LocalDate.now()), formatter.print(LocalDate.now()) });
|
new Object[] { LoanStatus.ACTIVE.getValue(), currentdate, currentdate, currentdate });
|
||||||
} catch (final EmptyResultDataAccessException e) {
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-13
@@ -492,8 +492,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
if (installment.getId() == null) {
|
if (installment.getId() == null) {
|
||||||
this.repaymentScheduleInstallmentRepository.save(installment);
|
this.repaymentScheduleInstallmentRepository.save(installment);
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.loanRepository.saveAndFlush(loan);
|
this.loanRepository.saveAndFlush(loan);
|
||||||
@@ -515,9 +513,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
if (installment.getId() == null) {
|
if (installment.getId() == null) {
|
||||||
this.repaymentScheduleInstallmentRepository.save(installment);
|
this.repaymentScheduleInstallmentRepository.save(installment);
|
||||||
} else {
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.loanRepository.save(loan);
|
this.loanRepository.save(loan);
|
||||||
} catch (final DataIntegrityViolationException e) {
|
} catch (final DataIntegrityViolationException e) {
|
||||||
@@ -893,6 +889,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
|
recalculateFrom = transactionToAdjust.getTransactionDate().isAfter(transactionDate) ? transactionDate : transactionToAdjust
|
||||||
|
.getTransactionDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||||
@@ -1040,6 +1038,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||||
workingDays = this.workingDaysRepository.findOne();
|
workingDays = this.workingDaysRepository.findOne();
|
||||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
|
recalculateFrom = transactionDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
@@ -1135,6 +1134,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
|
recalculateFrom = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
}
|
}
|
||||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
@@ -1217,6 +1217,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
|
recalculateFrom = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
}
|
}
|
||||||
|
|
||||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
@@ -1356,6 +1357,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
boolean isAppliedOnBackDate = false;
|
boolean isAppliedOnBackDate = false;
|
||||||
LoanCharge loanCharge = null;
|
LoanCharge loanCharge = null;
|
||||||
|
LocalDate recalculateFrom =loan.fetchInterestRecalculateFromDate();
|
||||||
if (chargeDefinition.isPercentageOfDisbursementAmount()) {
|
if (chargeDefinition.isPercentageOfDisbursementAmount()) {
|
||||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
|
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
|
||||||
for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
|
for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
|
||||||
@@ -1367,7 +1369,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE,
|
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE,
|
||||||
constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge));
|
constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge));
|
||||||
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
|
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
|
||||||
isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
addCharge(loan, chargeDefinition, loanCharge);
|
||||||
|
isAppliedOnBackDate = true;
|
||||||
|
if (recalculateFrom.isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate())) {
|
||||||
|
recalculateFrom = disbursementDetail.expectedDisbursementDateAsLocalDate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loan.addTrancheLoanCharge(chargeDefinition);
|
loan.addTrancheLoanCharge(chargeDefinition);
|
||||||
@@ -1378,12 +1384,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
|
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
|
||||||
isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
||||||
|
if (loanCharge.getDueLocalDate() == null || recalculateFrom.isAfter(loanCharge.getDueLocalDate())) {
|
||||||
|
isAppliedOnBackDate = true;
|
||||||
|
recalculateFrom = loanCharge.getDueLocalDate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean reprocessRequired = true;
|
boolean reprocessRequired = true;
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
||||||
runScheduleRecalculation(loan);
|
|
||||||
|
runScheduleRecalculation(loan, recalculateFrom);
|
||||||
reprocessRequired = false;
|
reprocessRequired = false;
|
||||||
}
|
}
|
||||||
updateOriginalSchedule(loan);
|
updateOriginalSchedule(loan);
|
||||||
@@ -1445,7 +1456,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runScheduleRecalculation(Loan loan) {
|
public void runScheduleRecalculation(final Loan loan, final LocalDate recalculateFrom) {
|
||||||
AppUser currentUser = getAppUserIfPresent();
|
AppUser currentUser = getAppUserIfPresent();
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
CalendarInstance restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
CalendarInstance restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||||
@@ -1466,7 +1477,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
WorkingDays workingDays = this.workingDaysRepository.findOne();
|
WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
final Long overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
final Long overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
|
|
||||||
LocalDate recalculateFrom = null;
|
|
||||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
ScheduleGeneratorDTO generatorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
ScheduleGeneratorDTO generatorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||||
@@ -2413,6 +2423,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||||
boolean runInterestRecalculation = false;
|
boolean runInterestRecalculation = false;
|
||||||
|
LocalDate recalculateFrom = DateUtils.getLocalDateOfTenant();
|
||||||
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduleDatas) {
|
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduleDatas) {
|
||||||
|
|
||||||
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString());
|
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString());
|
||||||
@@ -2422,12 +2433,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
overdueInstallment.getPeriodNumber(), command, loan, existingTransactionIds, existingReversedTransactionIds);
|
overdueInstallment.getPeriodNumber(), command, loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
loan = overdueDTO.getLoan();
|
loan = overdueDTO.getLoan();
|
||||||
runInterestRecalculation = runInterestRecalculation || overdueDTO.isRunInterestRecalculation();
|
runInterestRecalculation = runInterestRecalculation || overdueDTO.isRunInterestRecalculation();
|
||||||
|
if (recalculateFrom.isAfter(overdueDTO.getRecalculateFrom())) {
|
||||||
|
recalculateFrom = overdueDTO.getRecalculateFrom();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (loan != null) {
|
if (loan != null) {
|
||||||
boolean reprocessRequired = true;
|
boolean reprocessRequired = true;
|
||||||
|
LocalDate recalculatedTill = loan.fetchInterestRecalculateFromDate();
|
||||||
|
if(recalculateFrom.isAfter(recalculatedTill)){
|
||||||
|
recalculateFrom = recalculatedTill;
|
||||||
|
}
|
||||||
|
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
if (runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
if (runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
||||||
runScheduleRecalculation(loan);
|
runScheduleRecalculation(loan, recalculateFrom);
|
||||||
reprocessRequired = false;
|
reprocessRequired = false;
|
||||||
}
|
}
|
||||||
updateOriginalSchedule(loan);
|
updateOriginalSchedule(loan);
|
||||||
@@ -2482,7 +2501,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
if (feeFrequency == null) {
|
if (feeFrequency == null) {
|
||||||
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff.intValue()));
|
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff.intValue()));
|
||||||
} else {
|
} else {
|
||||||
while (new LocalDate().isAfter(startDate)) {
|
while (DateUtils.getLocalDateOfTenant().isAfter(startDate)) {
|
||||||
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff.intValue()));
|
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff.intValue()));
|
||||||
LocalDate scheduleDate = scheduledDateGenerator.getRepaymentPeriodDate(PeriodFrequencyType.fromInt(feeFrequency),
|
LocalDate scheduleDate = scheduledDateGenerator.getRepaymentPeriodDate(PeriodFrequencyType.fromInt(feeFrequency),
|
||||||
chargeDefinition.feeInterval(), startDate, null, null);
|
chargeDefinition.feeInterval(), startDate, null, null);
|
||||||
@@ -2505,6 +2524,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
installment = loan.fetchRepaymentScheduleInstallment(periodNumber);
|
installment = loan.fetchRepaymentScheduleInstallment(periodNumber);
|
||||||
}
|
}
|
||||||
|
LocalDate recalculateFrom = DateUtils.getLocalDateOfTenant();
|
||||||
if (loan != null) {
|
if (loan != null) {
|
||||||
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_APPLY_OVERDUE_CHARGE,
|
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_APPLY_OVERDUE_CHARGE,
|
||||||
constructEntityMap(BUSINESS_ENTITY.LOAN, loan));
|
constructEntityMap(BUSINESS_ENTITY.LOAN, loan));
|
||||||
@@ -2518,10 +2538,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
boolean isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
boolean isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
||||||
runInterestRecalculation = runInterestRecalculation || isAppliedOnBackDate;
|
runInterestRecalculation = runInterestRecalculation || isAppliedOnBackDate;
|
||||||
|
if (entry.getValue().isBefore(recalculateFrom)) {
|
||||||
|
recalculateFrom = entry.getValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LoanOverdueDTO(loan, runInterestRecalculation);
|
return new LoanOverdueDTO(loan, runInterestRecalculation, recalculateFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2845,7 +2868,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
List<Holiday> holidays = null;
|
List<Holiday> holidays = null;
|
||||||
boolean isHolidayEnabled = false;
|
boolean isHolidayEnabled = false;
|
||||||
WorkingDays workingDays = null;
|
WorkingDays workingDays = null;
|
||||||
LocalDate recalculateFrom = null;
|
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
|
||||||
Long overdurPenaltyWaitPeriod = null;
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||||
|
|||||||
+9
-1
@@ -5,16 +5,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanproduct.data;
|
package org.mifosplatform.portfolio.loanproduct.data;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
|
|
||||||
public class LoanOverdueDTO {
|
public class LoanOverdueDTO {
|
||||||
|
|
||||||
private final Loan loan;
|
private final Loan loan;
|
||||||
private final boolean runInterestRecalculation;
|
private final boolean runInterestRecalculation;
|
||||||
|
private final LocalDate recalculateFrom;
|
||||||
|
|
||||||
public LoanOverdueDTO(final Loan loan, final boolean runInterestRecalculation) {
|
public LoanOverdueDTO(final Loan loan, final boolean runInterestRecalculation, final LocalDate recalculateFrom) {
|
||||||
this.loan = loan;
|
this.loan = loan;
|
||||||
this.runInterestRecalculation = runInterestRecalculation;
|
this.runInterestRecalculation = runInterestRecalculation;
|
||||||
|
this.recalculateFrom = recalculateFrom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRunInterestRecalculation() {
|
public boolean isRunInterestRecalculation() {
|
||||||
@@ -24,4 +27,9 @@ public class LoanOverdueDTO {
|
|||||||
public Loan getLoan() {
|
public Loan getLoan() {
|
||||||
return this.loan;
|
return this.loan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public LocalDate getRecalculateFrom() {
|
||||||
|
return this.recalculateFrom;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `m_loan`
|
||||||
|
ADD COLUMN `interest_recalcualated_on` DATE NULL DEFAULT NULL AFTER `accrued_till`;
|
||||||
Reference in New Issue
Block a user