FINERACT-2190: Avoid recreation of loan repayment schedule periods
This commit is contained in:
committed by
Adam Saghy
parent
b9cfa470ff
commit
52c41b1471
-51
@@ -57,7 +57,6 @@ import java.util.List;
|
|||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -112,7 +111,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
|
|||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
|
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
|
import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||||
@@ -610,8 +608,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate();
|
this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate();
|
||||||
this.submittedOnDate = submittedOnDate != null ? submittedOnDate : DateUtils.getBusinessLocalDate();
|
this.submittedOnDate = submittedOnDate != null ? submittedOnDate : DateUtils.getBusinessLocalDate();
|
||||||
|
|
||||||
updateLoanSchedule(loanScheduleModel);
|
|
||||||
|
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
|
|
||||||
// Copy interest recalculation settings if interest recalculation is enabled
|
// Copy interest recalculation settings if interest recalculation is enabled
|
||||||
@@ -1005,53 +1001,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
this.rates.addAll(loanRates);
|
this.rates.addAll(loanRates);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) {
|
|
||||||
this.repaymentScheduleInstallments.clear();
|
|
||||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
|
||||||
|
|
||||||
if (scheduledLoanInstallment.isRepaymentPeriod() || scheduledLoanInstallment.isDownPaymentPeriod()) {
|
|
||||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this,
|
|
||||||
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
|
||||||
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
|
||||||
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
|
||||||
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent(),
|
|
||||||
scheduledLoanInstallment.getLoanCompoundingDetails(), scheduledLoanInstallment.rescheduleInterestPortion(),
|
|
||||||
scheduledLoanInstallment.isDownPaymentPeriod());
|
|
||||||
addLoanRepaymentScheduleInstallment(installment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateLoanScheduleDependentDerivedFields();
|
|
||||||
updateLoanSummaryDerivedFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLoanSchedule(final Collection<LoanRepaymentScheduleInstallment> installments) {
|
|
||||||
List<LoanRepaymentScheduleInstallment> existingInstallments = new ArrayList<>(this.repaymentScheduleInstallments);
|
|
||||||
repaymentScheduleInstallments.clear();
|
|
||||||
for (final LoanRepaymentScheduleInstallment installment : installments) {
|
|
||||||
LoanRepaymentScheduleInstallment existingInstallment = findByInstallmentNumber(existingInstallments,
|
|
||||||
installment.getInstallmentNumber());
|
|
||||||
if (existingInstallment != null) {
|
|
||||||
Set<LoanInstallmentCharge> existingCharges = existingInstallment.getInstallmentCharges();
|
|
||||||
installment.getInstallmentCharges().addAll(existingCharges);
|
|
||||||
existingCharges.forEach(c -> c.setInstallment(installment));
|
|
||||||
existingInstallment.getInstallmentCharges().clear();
|
|
||||||
}
|
|
||||||
addLoanRepaymentScheduleInstallment(installment);
|
|
||||||
}
|
|
||||||
updateLoanScheduleDependentDerivedFields();
|
|
||||||
updateLoanSummaryDerivedFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoanRepaymentScheduleInstallment findByInstallmentNumber(Collection<LoanRepaymentScheduleInstallment> installments,
|
|
||||||
Integer installmentNumber) {
|
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
|
||||||
if (Objects.equals(installment.getInstallmentNumber(), installmentNumber)) {
|
|
||||||
return installment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLoanScheduleDependentDerivedFields() {
|
public void updateLoanScheduleDependentDerivedFields() {
|
||||||
if (this.getLoanRepaymentScheduleInstallmentsSize() > 0) {
|
if (this.getLoanRepaymentScheduleInstallmentsSize() > 0) {
|
||||||
this.expectedMaturityDate = determineExpectedMaturityDate();
|
this.expectedMaturityDate = determineExpectedMaturityDate();
|
||||||
|
|||||||
+81
-25
@@ -38,10 +38,12 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
|||||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
|
import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
|
||||||
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks;
|
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@Setter
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "m_loan_repayment_schedule")
|
@Table(name = "m_loan_repayment_schedule")
|
||||||
public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
||||||
@@ -60,67 +62,67 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
|||||||
@Column(name = "duedate", nullable = false)
|
@Column(name = "duedate", nullable = false)
|
||||||
private LocalDate dueDate;
|
private LocalDate dueDate;
|
||||||
|
|
||||||
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "principal_amount", scale = 6, precision = 19)
|
||||||
private BigDecimal principal;
|
private BigDecimal principal;
|
||||||
|
|
||||||
@Column(name = "principal_completed_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "principal_completed_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal principalCompleted;
|
private BigDecimal principalCompleted;
|
||||||
|
|
||||||
@Column(name = "principal_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "principal_writtenoff_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal principalWrittenOff;
|
private BigDecimal principalWrittenOff;
|
||||||
|
|
||||||
@Column(name = "interest_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "interest_amount", scale = 6, precision = 19)
|
||||||
private BigDecimal interestCharged;
|
private BigDecimal interestCharged;
|
||||||
|
|
||||||
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "interest_completed_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal interestPaid;
|
private BigDecimal interestPaid;
|
||||||
|
|
||||||
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "interest_waived_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal interestWaived;
|
private BigDecimal interestWaived;
|
||||||
|
|
||||||
@Column(name = "interest_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "interest_writtenoff_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal interestWrittenOff;
|
private BigDecimal interestWrittenOff;
|
||||||
|
|
||||||
@Column(name = "accrual_interest_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "accrual_interest_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal interestAccrued;
|
private BigDecimal interestAccrued;
|
||||||
|
|
||||||
@Column(name = "reschedule_interest_portion", scale = 6, precision = 19, nullable = true)
|
@Column(name = "reschedule_interest_portion", scale = 6, precision = 19)
|
||||||
private BigDecimal rescheduleInterestPortion;
|
private BigDecimal rescheduleInterestPortion;
|
||||||
|
|
||||||
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "fee_charges_amount", scale = 6, precision = 19)
|
||||||
private BigDecimal feeChargesCharged;
|
private BigDecimal feeChargesCharged;
|
||||||
|
|
||||||
@Column(name = "fee_charges_completed_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "fee_charges_completed_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal feeChargesPaid;
|
private BigDecimal feeChargesPaid;
|
||||||
|
|
||||||
@Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal feeChargesWrittenOff;
|
private BigDecimal feeChargesWrittenOff;
|
||||||
|
|
||||||
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal feeChargesWaived;
|
private BigDecimal feeChargesWaived;
|
||||||
|
|
||||||
@Column(name = "accrual_fee_charges_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "accrual_fee_charges_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal feeAccrued;
|
private BigDecimal feeAccrued;
|
||||||
|
|
||||||
@Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "penalty_charges_amount", scale = 6, precision = 19)
|
||||||
private BigDecimal penaltyCharges;
|
private BigDecimal penaltyCharges;
|
||||||
|
|
||||||
@Column(name = "penalty_charges_completed_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "penalty_charges_completed_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal penaltyChargesPaid;
|
private BigDecimal penaltyChargesPaid;
|
||||||
|
|
||||||
@Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal penaltyChargesWrittenOff;
|
private BigDecimal penaltyChargesWrittenOff;
|
||||||
|
|
||||||
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal penaltyChargesWaived;
|
private BigDecimal penaltyChargesWaived;
|
||||||
|
|
||||||
@Column(name = "accrual_penalty_charges_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "accrual_penalty_charges_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal penaltyAccrued;
|
private BigDecimal penaltyAccrued;
|
||||||
|
|
||||||
@Column(name = "total_paid_in_advance_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "total_paid_in_advance_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal totalPaidInAdvance;
|
private BigDecimal totalPaidInAdvance;
|
||||||
|
|
||||||
@Column(name = "total_paid_late_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "total_paid_late_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal totalPaidLate;
|
private BigDecimal totalPaidLate;
|
||||||
|
|
||||||
@Column(name = "completed_derived", nullable = false)
|
@Column(name = "completed_derived", nullable = false)
|
||||||
@@ -139,17 +141,17 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
|||||||
// approach
|
// approach
|
||||||
// https://blog.thepete.net/blog/2023/12/05/expand/contract-making-a-breaking-change-without-a-big-bang/
|
// https://blog.thepete.net/blog/2023/12/05/expand/contract-making-a-breaking-change-without-a-big-bang/
|
||||||
@Setter
|
@Setter
|
||||||
@Column(name = "credits_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "credits_amount", scale = 6, precision = 19)
|
||||||
private BigDecimal creditedPrincipal;
|
private BigDecimal creditedPrincipal;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@Column(name = "credited_interest", scale = 6, precision = 19, nullable = true)
|
@Column(name = "credited_interest", scale = 6, precision = 19)
|
||||||
private BigDecimal creditedInterest;
|
private BigDecimal creditedInterest;
|
||||||
|
|
||||||
@Column(name = "credited_fee", scale = 6, precision = 19, nullable = true)
|
@Column(name = "credited_fee", scale = 6, precision = 19)
|
||||||
private BigDecimal creditedFee;
|
private BigDecimal creditedFee;
|
||||||
|
|
||||||
@Column(name = "credited_penalty", scale = 6, precision = 19, nullable = true)
|
@Column(name = "credited_penalty", scale = 6, precision = 19)
|
||||||
private BigDecimal creditedPenalty;
|
private BigDecimal creditedPenalty;
|
||||||
|
|
||||||
@Column(name = "is_down_payment", nullable = false)
|
@Column(name = "is_down_payment", nullable = false)
|
||||||
@@ -1068,4 +1070,58 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
|||||||
MathUtil.nullToZero(MathUtil.add(getPrincipal(), getInterestCharged(), getFeeChargesCharged(), getPenaltyCharges())));
|
MathUtil.nullToZero(MathUtil.add(getPrincipal(), getInterestCharged(), getFeeChargesCharged(), getPenaltyCharges())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void copyFrom(final LoanScheduleModelPeriod period) {
|
||||||
|
resetBalances();
|
||||||
|
setFromDate(period.periodFromDate());
|
||||||
|
setDueDate(period.periodDueDate());
|
||||||
|
setPrincipal(period.principalDue());
|
||||||
|
setInterestCharged(period.interestDue());
|
||||||
|
setFeeChargesCharged(period.feeChargesDue());
|
||||||
|
setPenaltyCharges(period.penaltyChargesDue());
|
||||||
|
|
||||||
|
setRecalculatedInterestComponent(period.isRecalculatedInterestComponent());
|
||||||
|
if (getLoanCompoundingDetails() != null) {
|
||||||
|
getLoanCompoundingDetails().clear();
|
||||||
|
}
|
||||||
|
if (period.getLoanCompoundingDetails() != null) {
|
||||||
|
period.getLoanCompoundingDetails().forEach(lcd -> {
|
||||||
|
lcd.setLoanRepaymentScheduleInstallment(this);
|
||||||
|
getLoanCompoundingDetails().add(lcd);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setRescheduleInterestPortion(period.rescheduleInterestPortion());
|
||||||
|
setDownPayment(period.isDownPaymentPeriod());
|
||||||
|
|
||||||
|
setAdditional(false);
|
||||||
|
setReAged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyFrom(final LoanRepaymentScheduleInstallment installment) {
|
||||||
|
setFromDate(installment.getFromDate());
|
||||||
|
setDueDate(installment.getDueDate());
|
||||||
|
|
||||||
|
setPrincipal(installment.getPrincipal());
|
||||||
|
setInterestCharged(installment.getInterestCharged());
|
||||||
|
setFeeChargesCharged(installment.getFeeChargesCharged());
|
||||||
|
setPenaltyCharges(installment.getPenaltyCharges());
|
||||||
|
setRecalculatedInterestComponent(installment.isRecalculatedInterestComponent());
|
||||||
|
if (getLoanCompoundingDetails() != null) {
|
||||||
|
getLoanCompoundingDetails().clear();
|
||||||
|
}
|
||||||
|
if (installment.getLoanCompoundingDetails() != null) {
|
||||||
|
installment.getLoanCompoundingDetails().forEach(lcd -> {
|
||||||
|
lcd.setLoanRepaymentScheduleInstallment(this);
|
||||||
|
getLoanCompoundingDetails().add(lcd);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<LoanInstallmentCharge> installmentCharges = installment.getInstallmentCharges();
|
||||||
|
installmentCharges.forEach(c -> c.setInstallment(this));
|
||||||
|
getInstallmentCharges().addAll(installmentCharges);
|
||||||
|
|
||||||
|
setAdditional(installment.isAdditional());
|
||||||
|
setReAged(installment.isReAged());
|
||||||
|
setDownPayment(installment.isDownPayment());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -29,6 +29,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LoanScheduleService {
|
public class LoanScheduleService {
|
||||||
@@ -36,16 +37,17 @@ public class LoanScheduleService {
|
|||||||
private final LoanChargeService loanChargeService;
|
private final LoanChargeService loanChargeService;
|
||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
private final LoanTransactionProcessingService loadTransactionProcessingService;
|
private final LoanTransactionProcessingService loadTransactionProcessingService;
|
||||||
|
private final LoanScheduleComponent loanSchedule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ability to regenerate the repayment schedule based on the loans current details/state.
|
* Ability to regenerate the repayment schedule based on the loans current details/state.
|
||||||
*/
|
*/
|
||||||
public void regenerateRepaymentSchedule(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
public void regenerateRepaymentSchedule(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final LoanScheduleModel loanSchedule = loan.regenerateScheduleModel(scheduleGeneratorDTO);
|
final LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(scheduleGeneratorDTO);
|
||||||
if (loanSchedule == null) {
|
if (loanScheduleModel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loan.updateLoanSchedule(loanSchedule);
|
loanSchedule.updateLoanSchedule(loan, loanScheduleModel);
|
||||||
final Set<LoanCharge> charges = loan.getActiveCharges();
|
final Set<LoanCharge> charges = loan.getActiveCharges();
|
||||||
for (final LoanCharge loanCharge : charges) {
|
for (final LoanCharge loanCharge : charges) {
|
||||||
if (!loanCharge.isWaived()) {
|
if (!loanCharge.isWaived()) {
|
||||||
@@ -80,15 +82,15 @@ public class LoanScheduleService {
|
|||||||
|
|
||||||
public void regenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
public void regenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
||||||
final LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
|
final LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
|
||||||
final LoanScheduleDTO loanSchedule = loadTransactionProcessingService.getRecalculatedSchedule(generatorDTO, loan);
|
final LoanScheduleDTO loanScheduleDTO = loadTransactionProcessingService.getRecalculatedSchedule(generatorDTO, loan);
|
||||||
if (loanSchedule == null) {
|
if (loanScheduleDTO == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Either the installments got recalculated or the model
|
// Either the installments got recalculated or the model
|
||||||
if (loanSchedule.getInstallments() != null) {
|
if (loanScheduleDTO.getInstallments() != null) {
|
||||||
loan.updateLoanSchedule(loanSchedule.getInstallments());
|
loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getInstallments());
|
||||||
} else {
|
} else {
|
||||||
loan.updateLoanSchedule(loanSchedule.getLoanScheduleModel());
|
loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getLoanScheduleModel());
|
||||||
}
|
}
|
||||||
loan.setInterestRecalculatedOn(DateUtils.getBusinessLocalDate());
|
loan.setInterestRecalculatedOn(DateUtils.getBusinessLocalDate());
|
||||||
final LocalDate lastRepaymentDate = loan.getLastRepaymentPeriodDueDate(true);
|
final LocalDate lastRepaymentDate = loan.getLastRepaymentPeriodDueDate(true);
|
||||||
|
|||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.service.schedule;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LoanScheduleComponent {
|
||||||
|
|
||||||
|
public void updateLoanSchedule(Loan loan, final LoanScheduleModel modifiedLoanSchedule) {
|
||||||
|
final List<LoanScheduleModelPeriod> periods = modifiedLoanSchedule.getPeriods();
|
||||||
|
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
||||||
|
if (scheduledLoanInstallment.isRepaymentPeriod() || scheduledLoanInstallment.isDownPaymentPeriod()) {
|
||||||
|
LoanRepaymentScheduleInstallment existingInstallment = findByInstallmentNumber(loan.getRepaymentScheduleInstallments(),
|
||||||
|
scheduledLoanInstallment.periodNumber());
|
||||||
|
if (existingInstallment == null) {
|
||||||
|
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan,
|
||||||
|
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
||||||
|
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
||||||
|
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
||||||
|
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent(),
|
||||||
|
scheduledLoanInstallment.getLoanCompoundingDetails(), scheduledLoanInstallment.rescheduleInterestPortion(),
|
||||||
|
scheduledLoanInstallment.isDownPaymentPeriod());
|
||||||
|
loan.addLoanRepaymentScheduleInstallment(installment);
|
||||||
|
} else {
|
||||||
|
existingInstallment.copyFrom(scheduledLoanInstallment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Review Installments removed
|
||||||
|
loan.getRepaymentScheduleInstallments().removeIf(i -> !existInstallment(periods, i.getInstallmentNumber()));
|
||||||
|
|
||||||
|
loan.updateLoanScheduleDependentDerivedFields();
|
||||||
|
loan.updateLoanSummaryDerivedFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateLoanSchedule(Loan loan, final List<LoanRepaymentScheduleInstallment> installments) {
|
||||||
|
for (final LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
|
LoanRepaymentScheduleInstallment existingInstallment = findByInstallmentNumber(loan.getRepaymentScheduleInstallments(),
|
||||||
|
installment.getInstallmentNumber());
|
||||||
|
if (existingInstallment != null) {
|
||||||
|
existingInstallment.copyFrom(installment);
|
||||||
|
} else {
|
||||||
|
loan.addLoanRepaymentScheduleInstallment(installment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Review Installments removed
|
||||||
|
loan.getRepaymentScheduleInstallments().removeIf(i -> !existInstallment(installments, i.getInstallmentNumber()));
|
||||||
|
|
||||||
|
loan.updateLoanScheduleDependentDerivedFields();
|
||||||
|
loan.updateLoanSummaryDerivedFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanRepaymentScheduleInstallment findByInstallmentNumber(final Collection<LoanRepaymentScheduleInstallment> installments,
|
||||||
|
final Integer installmentNumber) {
|
||||||
|
return installments.stream().filter(i -> installmentNumber.compareTo(i.getInstallmentNumber()) == 0).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean existInstallment(final Collection<LoanRepaymentScheduleInstallment> installments, final Integer installmentNumber) {
|
||||||
|
return installments.stream().anyMatch(i -> installmentNumber.compareTo(i.getInstallmentNumber()) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean existInstallment(final List<LoanScheduleModelPeriod> periods, final Integer installmentNumber) {
|
||||||
|
return periods.stream().anyMatch(p -> p.periodNumber() != null && installmentNumber.compareTo(p.periodNumber()) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+5
-3
@@ -91,6 +91,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Mon
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService;
|
import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails;
|
import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||||
@@ -114,13 +115,15 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
private final EMICalculator emiCalculator;
|
private final EMICalculator emiCalculator;
|
||||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||||
private final InterestRefundService interestRefundService;
|
private final InterestRefundService interestRefundService;
|
||||||
|
private final LoanScheduleComponent loanSchedule;
|
||||||
|
|
||||||
public AdvancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator, LoanRepositoryWrapper loanRepositoryWrapper,
|
public AdvancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator, LoanRepositoryWrapper loanRepositoryWrapper,
|
||||||
InterestRefundService interestRefundService, ExternalIdFactory externalIdFactory) {
|
InterestRefundService interestRefundService, ExternalIdFactory externalIdFactory, LoanScheduleComponent loanSchedule) {
|
||||||
super(externalIdFactory);
|
super(externalIdFactory);
|
||||||
this.emiCalculator = emiCalculator;
|
this.emiCalculator = emiCalculator;
|
||||||
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
||||||
this.interestRefundService = interestRefundService;
|
this.interestRefundService = interestRefundService;
|
||||||
|
this.loanSchedule = loanSchedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1460,8 +1463,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loan.updateLoanSchedule(installmentsUpToTransactionDate);
|
loanSchedule.updateLoanSchedule(loan, installmentsUpToTransactionDate);
|
||||||
loan.updateLoanScheduleDependentDerivedFields();
|
|
||||||
|
|
||||||
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx && loan.isInterestRecalculationEnabled()) {
|
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx && loan.isInterestRecalculationEnabled()) {
|
||||||
updateRepaymentPeriodsAfterChargeOff(progressiveTransactionCtx, transactionDate, transactionsToBeReprocessed);
|
updateRepaymentPeriodsAfterChargeOff(progressiveTransactionCtx, transactionDate, transactionsToBeReprocessed);
|
||||||
|
|||||||
+3
-1
@@ -69,6 +69,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
|
import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
|
||||||
@@ -98,6 +99,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
|||||||
private AdvancedPaymentScheduleTransactionProcessor underTest;
|
private AdvancedPaymentScheduleTransactionProcessor underTest;
|
||||||
private static final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class);
|
private static final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class);
|
||||||
private static final LoanRepositoryWrapper loanRepositoryWrapper = Mockito.mock(LoanRepositoryWrapper.class);
|
private static final LoanRepositoryWrapper loanRepositoryWrapper = Mockito.mock(LoanRepositoryWrapper.class);
|
||||||
|
private static final LoanScheduleComponent loanSchedule = Mockito.mock(LoanScheduleComponent.class);
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void init() {
|
public static void init() {
|
||||||
@@ -112,7 +114,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, null, null);
|
underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, null, null, loanSchedule);
|
||||||
|
|
||||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||||
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
|
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
|
||||||
|
|||||||
+6
-4
@@ -79,6 +79,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -115,6 +116,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
|||||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||||
private final LoanChargeService loanChargeService;
|
private final LoanChargeService loanChargeService;
|
||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
|
private final LoanScheduleComponent loanSchedule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object and persist
|
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object and persist
|
||||||
@@ -423,14 +425,14 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
|||||||
loanApplicationTerms.getInterestMethod());
|
loanApplicationTerms.getInterestMethod());
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
|
final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
|
||||||
loan.setHelpers(loanLifecycleStateMachine);
|
loan.setHelpers(loanLifecycleStateMachine);
|
||||||
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan,
|
final LoanScheduleDTO loanScheduleDTO = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan,
|
||||||
loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
||||||
|
|
||||||
// Either the installments got recalculated or the model
|
// Either the installments got recalculated or the model
|
||||||
if (loanSchedule.getInstallments() != null) {
|
if (loanScheduleDTO.getInstallments() != null) {
|
||||||
loan.updateLoanSchedule(loanSchedule.getInstallments());
|
loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getInstallments());
|
||||||
} else {
|
} else {
|
||||||
loan.updateLoanSchedule(loanSchedule.getLoanScheduleModel());
|
loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getLoanScheduleModel());
|
||||||
}
|
}
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
loanChargeService.recalculateAllCharges(loan);
|
loanChargeService.recalculateAllCharges(loan);
|
||||||
|
|||||||
+6
-2
@@ -93,6 +93,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanSchedu
|
|||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
|
import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
|
import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||||
@@ -137,6 +138,7 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
|||||||
private final LoanDisbursementService loanDisbursementService;
|
private final LoanDisbursementService loanDisbursementService;
|
||||||
private final LoanChargeService loanChargeService;
|
private final LoanChargeService loanChargeService;
|
||||||
private final LoanOfficerService loanOfficerService;
|
private final LoanOfficerService loanOfficerService;
|
||||||
|
private final LoanScheduleComponent loanSchedule;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loan assembleFrom(final Long accountId) {
|
public Loan assembleFrom(final Long accountId) {
|
||||||
@@ -293,6 +295,8 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
|||||||
throw new IllegalStateException("No loan application exists for either a client or group (or both).");
|
throw new IllegalStateException("No loan application exists for either a client or group (or both).");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loanSchedule.updateLoanSchedule(loanApplication, loanScheduleModel);
|
||||||
|
|
||||||
copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication);
|
copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication);
|
||||||
loanApplication.setHelpers(defaultLoanLifecycleStateMachine);
|
loanApplication.setHelpers(defaultLoanLifecycleStateMachine);
|
||||||
// TODO: review
|
// TODO: review
|
||||||
@@ -858,8 +862,8 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
|||||||
final JsonElement parsedQuery = this.fromApiJsonHelper.parse(command.json());
|
final JsonElement parsedQuery = this.fromApiJsonHelper.parse(command.json());
|
||||||
final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromApiJsonHelper);
|
final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromApiJsonHelper);
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query, false);
|
final LoanScheduleModel loanScheduleModel = this.calculationPlatformService.calculateLoanSchedule(query, false);
|
||||||
loan.updateLoanSchedule(loanSchedule);
|
loanSchedule.updateLoanSchedule(loan, loanScheduleModel);
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
loanChargeService.recalculateAllCharges(loan);
|
loanChargeService.recalculateAllCharges(loan);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -34,6 +34,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanInterestRefundServiceImpl;
|
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanInterestRefundServiceImpl;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -118,8 +119,9 @@ public class LoanAccountAutoStarter {
|
|||||||
@Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class)
|
@Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class)
|
||||||
public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator,
|
public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator,
|
||||||
LoanRepositoryWrapper loanRepositoryWrapper,
|
LoanRepositoryWrapper loanRepositoryWrapper,
|
||||||
@Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService, ExternalIdFactory externalIdFactory) {
|
@Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService, ExternalIdFactory externalIdFactory,
|
||||||
|
LoanScheduleComponent loanSchedule) {
|
||||||
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService,
|
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService,
|
||||||
externalIdFactory);
|
externalIdFactory, loanSchedule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -146,6 +146,7 @@ import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusi
|
|||||||
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventServiceImpl;
|
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventServiceImpl;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.adjustment.LoanAdjustmentService;
|
import org.apache.fineract.portfolio.loanaccount.service.adjustment.LoanAdjustmentService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||||
import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
|
import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
||||||
@@ -240,14 +241,15 @@ public class LoanAccountConfiguration {
|
|||||||
LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService,
|
LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||||
LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler, LoanChargeMapper loanChargeMapper,
|
LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler, LoanChargeMapper loanChargeMapper,
|
||||||
LoanCollateralManagementMapper loanCollateralManagementMapper, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
LoanCollateralManagementMapper loanCollateralManagementMapper, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||||
LoanDisbursementService loanDisbursementService, LoanChargeService loanChargeService, LoanOfficerService loanOfficerService) {
|
LoanDisbursementService loanDisbursementService, LoanChargeService loanChargeService, LoanOfficerService loanOfficerService,
|
||||||
|
LoanScheduleComponent loanSchedule) {
|
||||||
return new LoanAssemblerImpl(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
|
return new LoanAssemblerImpl(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
|
||||||
fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler,
|
fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler,
|
||||||
loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService, workingDaysRepository,
|
loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService, workingDaysRepository,
|
||||||
rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository, glimRepository,
|
rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository, glimRepository,
|
||||||
accountNumberGenerator, glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService,
|
accountNumberGenerator, glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService,
|
||||||
loanDisbursementDetailsAssembler, loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService,
|
loanDisbursementDetailsAssembler, loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService,
|
||||||
loanDisbursementService, loanChargeService, loanOfficerService);
|
loanDisbursementService, loanChargeService, loanOfficerService, loanSchedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -461,8 +463,8 @@ public class LoanAccountConfiguration {
|
|||||||
@ConditionalOnMissingBean(LoanScheduleService.class)
|
@ConditionalOnMissingBean(LoanScheduleService.class)
|
||||||
public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService,
|
public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService,
|
||||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
LoanTransactionProcessingService loanTransactionProcessingService, LoanScheduleComponent loanSchedule) {
|
||||||
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanTransactionProcessingService);
|
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanTransactionProcessingService, loanSchedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
+6
-6
@@ -5310,8 +5310,8 @@ public class ClientLoanIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
|
|
||||||
ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID);
|
ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID);
|
||||||
assertEquals(2, loanSchedule.size());
|
assertEquals(2, loanSchedule.size());
|
||||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue"));
|
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||||
assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||||
assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||||
LocalDate targetDate = LocalDate.of(2022, 9, 7);
|
LocalDate targetDate = LocalDate.of(2022, 9, 7);
|
||||||
@@ -5906,10 +5906,10 @@ public class ClientLoanIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
|
|
||||||
ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID);
|
ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID);
|
||||||
assertEquals(2, loanSchedule.size());
|
assertEquals(2, loanSchedule.size());
|
||||||
assertEquals(0, loanSchedule.get(1).get("feeChargesDue"));
|
assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding"));
|
assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue"));
|
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||||
assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||||
assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||||
LocalDate targetDate = LocalDate.of(2022, 9, 7);
|
LocalDate targetDate = LocalDate.of(2022, 9, 7);
|
||||||
|
|||||||
+8
-8
@@ -173,10 +173,10 @@ public class LoanSpecificDueDateChargeAfterMaturityTest extends BaseLoanIntegrat
|
|||||||
|
|
||||||
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||||
assertEquals(2, loanSchedule.size());
|
assertEquals(2, loanSchedule.size());
|
||||||
assertEquals(0, loanSchedule.get(1).get("feeChargesDue"));
|
assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding"));
|
assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue"));
|
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||||
assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||||
assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||||
targetDate = LocalDate.of(2011, 4, 5);
|
targetDate = LocalDate.of(2011, 4, 5);
|
||||||
@@ -306,10 +306,10 @@ public class LoanSpecificDueDateChargeAfterMaturityTest extends BaseLoanIntegrat
|
|||||||
|
|
||||||
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||||
assertEquals(2, loanSchedule.size());
|
assertEquals(2, loanSchedule.size());
|
||||||
assertEquals(0, loanSchedule.get(1).get("feeChargesDue"));
|
assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding"));
|
assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue"));
|
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||||
assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||||
assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -844,7 +844,7 @@ public class SchedulerJobsTestResults extends IntegrationTest {
|
|||||||
ArrayList<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
|
ArrayList<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
|
||||||
loanID);
|
loanID);
|
||||||
|
|
||||||
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
Assertions.assertEquals(0.0f, repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
||||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1085,7 +1085,7 @@ public class SchedulerJobsTestResults extends IntegrationTest {
|
|||||||
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||||
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
|
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
|
||||||
loanID);
|
loanID);
|
||||||
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
Assertions.assertEquals(0.0f, repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
||||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||||
|
|
||||||
LocalDate lastBusinessDateBeforeFastForward = LocalDate.of(2019, 4, 2);
|
LocalDate lastBusinessDateBeforeFastForward = LocalDate.of(2019, 4, 2);
|
||||||
@@ -1153,7 +1153,7 @@ public class SchedulerJobsTestResults extends IntegrationTest {
|
|||||||
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||||
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
|
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
|
||||||
loanID2);
|
loanID2);
|
||||||
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
Assertions.assertEquals(0.0f, repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
||||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||||
|
|
||||||
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 5, 3));
|
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 5, 3));
|
||||||
|
|||||||
Reference in New Issue
Block a user