FINERACT-2090: restructure loan disbursements
more refactors
This commit is contained in:
committed by
Adam Saghy
parent
79f32d32a3
commit
c9d1b81cc6
+6
-8
@@ -158,15 +158,13 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep
|
|||||||
ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
|
ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
|
||||||
ExternalAssetOwnerTransferDetails details = new ExternalAssetOwnerTransferDetails();
|
ExternalAssetOwnerTransferDetails details = new ExternalAssetOwnerTransferDetails();
|
||||||
details.setExternalAssetOwnerTransfer(externalAssetOwnerTransfer);
|
details.setExternalAssetOwnerTransfer(externalAssetOwnerTransfer);
|
||||||
details.setTotalOutstanding(Objects.requireNonNullElse(loan.getLoanSummary().getTotalOutstanding(), BigDecimal.ZERO));
|
details.setTotalOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalOutstanding(), BigDecimal.ZERO));
|
||||||
details.setTotalPrincipalOutstanding(
|
details.setTotalPrincipalOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO));
|
||||||
Objects.requireNonNullElse(loan.getLoanSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO));
|
details.setTotalInterestOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalInterestOutstanding(), BigDecimal.ZERO));
|
||||||
details.setTotalInterestOutstanding(
|
|
||||||
Objects.requireNonNullElse(loan.getLoanSummary().getTotalInterestOutstanding(), BigDecimal.ZERO));
|
|
||||||
details.setTotalFeeChargesOutstanding(
|
details.setTotalFeeChargesOutstanding(
|
||||||
Objects.requireNonNullElse(loan.getLoanSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO));
|
Objects.requireNonNullElse(loan.getSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO));
|
||||||
details.setTotalPenaltyChargesOutstanding(
|
details.setTotalPenaltyChargesOutstanding(
|
||||||
Objects.requireNonNullElse(loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO));
|
Objects.requireNonNullElse(loan.getSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO));
|
||||||
details.setTotalOverpaid(Objects.requireNonNullElse(loan.getTotalOverpaid(), BigDecimal.ZERO));
|
details.setTotalOverpaid(Objects.requireNonNullElse(loan.getTotalOverpaid(), BigDecimal.ZERO));
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
@@ -179,7 +177,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isTransferable(final Loan loan) {
|
private boolean isTransferable(final Loan loan) {
|
||||||
return MathUtil.nullToDefault(loan.getLoanSummary().getTotalOutstanding(), BigDecimal.ZERO).compareTo(BigDecimal.ZERO) > 0;
|
return MathUtil.nullToDefault(loan.getSummary().getTotalOutstanding(), BigDecimal.ZERO).compareTo(BigDecimal.ZERO) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSameDaySaleAndBuyback(final LocalDate settlementDate, final List<ExternalAssetOwnerTransfer> transferDataList,
|
private void handleSameDaySaleAndBuyback(final LocalDate settlementDate, final List<ExternalAssetOwnerTransfer> transferDataList,
|
||||||
|
|||||||
+4
-4
@@ -80,10 +80,10 @@ public class AccountingServiceImpl implements AccountingService {
|
|||||||
// transaction properties
|
// transaction properties
|
||||||
final Long transactionId = transfer.getId();
|
final Long transactionId = transfer.getId();
|
||||||
final LocalDate transactionDate = transfer.getSettlementDate();
|
final LocalDate transactionDate = transfer.getSettlementDate();
|
||||||
final BigDecimal principalAmount = loan.getLoanSummary().getTotalPrincipalOutstanding();
|
final BigDecimal principalAmount = loan.getSummary().getTotalPrincipalOutstanding();
|
||||||
final BigDecimal interestAmount = loan.getLoanSummary().getTotalInterestOutstanding();
|
final BigDecimal interestAmount = loan.getSummary().getTotalInterestOutstanding();
|
||||||
final BigDecimal feesAmount = loan.getLoanSummary().getTotalFeeChargesOutstanding();
|
final BigDecimal feesAmount = loan.getSummary().getTotalFeeChargesOutstanding();
|
||||||
final BigDecimal penaltiesAmount = loan.getLoanSummary().getTotalPenaltyChargesOutstanding();
|
final BigDecimal penaltiesAmount = loan.getSummary().getTotalPenaltyChargesOutstanding();
|
||||||
final BigDecimal overPaymentAmount = loan.getTotalOverpaid();
|
final BigDecimal overPaymentAmount = loan.getTotalOverpaid();
|
||||||
|
|
||||||
// Moving money to asset transfer account
|
// Moving money to asset transfer account
|
||||||
|
|||||||
+5
-7
@@ -165,15 +165,13 @@ public class LoanAccountOwnerTransferServiceImpl implements LoanAccountOwnerTran
|
|||||||
ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
|
ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
|
||||||
ExternalAssetOwnerTransferDetails details = new ExternalAssetOwnerTransferDetails();
|
ExternalAssetOwnerTransferDetails details = new ExternalAssetOwnerTransferDetails();
|
||||||
details.setExternalAssetOwnerTransfer(externalAssetOwnerTransfer);
|
details.setExternalAssetOwnerTransfer(externalAssetOwnerTransfer);
|
||||||
details.setTotalOutstanding(Objects.requireNonNullElse(loan.getLoanSummary().getTotalOutstanding(), BigDecimal.ZERO));
|
details.setTotalOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalOutstanding(), BigDecimal.ZERO));
|
||||||
details.setTotalPrincipalOutstanding(
|
details.setTotalPrincipalOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO));
|
||||||
Objects.requireNonNullElse(loan.getLoanSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO));
|
details.setTotalInterestOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalInterestOutstanding(), BigDecimal.ZERO));
|
||||||
details.setTotalInterestOutstanding(
|
|
||||||
Objects.requireNonNullElse(loan.getLoanSummary().getTotalInterestOutstanding(), BigDecimal.ZERO));
|
|
||||||
details.setTotalFeeChargesOutstanding(
|
details.setTotalFeeChargesOutstanding(
|
||||||
Objects.requireNonNullElse(loan.getLoanSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO));
|
Objects.requireNonNullElse(loan.getSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO));
|
||||||
details.setTotalPenaltyChargesOutstanding(
|
details.setTotalPenaltyChargesOutstanding(
|
||||||
Objects.requireNonNullElse(loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO));
|
Objects.requireNonNullElse(loan.getSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO));
|
||||||
details.setTotalOverpaid(Objects.requireNonNullElse(loan.getTotalOverpaid(), BigDecimal.ZERO));
|
details.setTotalOverpaid(Objects.requireNonNullElse(loan.getTotalOverpaid(), BigDecimal.ZERO));
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -203,7 +203,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
|
|||||||
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||||
when(loanForProcessing.getId()).thenReturn(1L);
|
when(loanForProcessing.getId()).thenReturn(1L);
|
||||||
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
ExternalAssetOwnerTransfer firstResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
ExternalAssetOwnerTransfer firstResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
||||||
ExternalAssetOwnerTransfer secondResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
ExternalAssetOwnerTransfer secondResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
||||||
when(firstResponseItem.getStatus()).thenReturn(ExternalTransferStatus.BUYBACK);
|
when(firstResponseItem.getStatus()).thenReturn(ExternalTransferStatus.BUYBACK);
|
||||||
@@ -248,7 +248,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
|
|||||||
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
||||||
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
|
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
|
||||||
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ONE);
|
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ONE);
|
||||||
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.ACTIVE);
|
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.ACTIVE);
|
||||||
// when
|
// when
|
||||||
@@ -296,7 +296,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
|
|||||||
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
||||||
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
|
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
|
||||||
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
|
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
|
||||||
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ZERO);
|
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ZERO);
|
||||||
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.DECLINED);
|
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.DECLINED);
|
||||||
@@ -340,7 +340,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
|
|||||||
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
||||||
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
|
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
|
||||||
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ONE.negate());
|
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ONE.negate());
|
||||||
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ONE.negate());
|
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ONE.negate());
|
||||||
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.DECLINED);
|
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.DECLINED);
|
||||||
@@ -389,7 +389,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
|
|||||||
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||||
when(loanForProcessing.getId()).thenReturn(1L);
|
when(loanForProcessing.getId()).thenReturn(1L);
|
||||||
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
|
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
|
||||||
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ONE);
|
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ONE);
|
||||||
ExternalAssetOwnerTransfer firstResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
ExternalAssetOwnerTransfer firstResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
||||||
|
|||||||
+1
-1
@@ -133,7 +133,7 @@ public class LoanAccountOwnerTransferServiceTest {
|
|||||||
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||||
when(loanForProcessing.getId()).thenReturn(1L);
|
when(loanForProcessing.getId()).thenReturn(1L);
|
||||||
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
|
|
||||||
ExternalAssetOwnerTransfer pendingBuybackTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
ExternalAssetOwnerTransfer pendingBuybackTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
|
||||||
when(pendingBuybackTransfer.getStatus()).thenReturn(BUYBACK);
|
when(pendingBuybackTransfer.getStatus()).thenReturn(BUYBACK);
|
||||||
|
|||||||
+1
-1
@@ -85,7 +85,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
|||||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.APPROVED, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
|
if (anyOfAllowedWhenComingFrom(from, LoanStatus.APPROVED, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
|
||||||
newState = activeTransition();
|
newState = activeTransition();
|
||||||
} else if (from.isOverpaid() && loan.getTotalOverpaidAsMoney().isZero()) {
|
} else if (from.isOverpaid() && loan.getTotalOverpaidAsMoney().isZero()) {
|
||||||
if (loan.getLoanSummary().getTotalOutstanding(loan.getCurrency()).isZero()) {
|
if (loan.getSummary().getTotalOutstanding(loan.getCurrency()).isZero()) {
|
||||||
newState = closeObligationsMetTransition();
|
newState = closeObligationsMetTransition();
|
||||||
} else {
|
} else {
|
||||||
newState = activeTransition();
|
newState = activeTransition();
|
||||||
|
|||||||
+19
-156
@@ -613,7 +613,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return this.loanRepaymentScheduleDetail.getNumberOfRepayments();
|
return this.loanRepaymentScheduleDetail.getNumberOfRepayments();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanSummary updateSummaryWithTotalFeeChargesDueAtDisbursement(final BigDecimal feeChargesDueAtDisbursement) {
|
public LoanSummary updateSummaryWithTotalFeeChargesDueAtDisbursement(final BigDecimal feeChargesDueAtDisbursement) {
|
||||||
if (this.summary == null) {
|
if (this.summary == null) {
|
||||||
this.summary = LoanSummary.create(feeChargesDueAtDisbursement);
|
this.summary = LoanSummary.create(feeChargesDueAtDisbursement);
|
||||||
} else {
|
} else {
|
||||||
@@ -634,7 +634,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
this.summary.updateTotalWaived(this.summary.getTotalWaived().subtract(amountWaived));
|
this.summary.updateTotalWaived(this.summary.getTotalWaived().subtract(amountWaived));
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
|
public BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
|
||||||
return getActiveCharges().stream() //
|
return getActiveCharges().stream() //
|
||||||
.filter(LoanCharge::isDueAtDisbursement) //
|
.filter(LoanCharge::isDueAtDisbursement) //
|
||||||
.map(LoanCharge::amount) //
|
.map(LoanCharge::amount) //
|
||||||
@@ -1721,108 +1721,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
|
public List<LoanDisbursementDetails> getDisbursedLoanDisbursementDetails() {
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail) {
|
|
||||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE);
|
|
||||||
|
|
||||||
this.disbursedBy = currentUser;
|
|
||||||
updateLoanScheduleDependentDerivedFields();
|
|
||||||
|
|
||||||
actualChanges.put(LOCALE, command.locale());
|
|
||||||
actualChanges.put(DATE_FORMAT, command.dateFormat());
|
|
||||||
actualChanges.put(ACTUAL_DISBURSEMENT_DATE, command.stringValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE));
|
|
||||||
|
|
||||||
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
|
||||||
|
|
||||||
// validate if disbursement date is a holiday or a non-working day
|
|
||||||
validateDisbursementDateIsOnNonWorkingDay(holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
|
||||||
validateDisbursementDateIsOnHoliday(holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays());
|
|
||||||
|
|
||||||
regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(this.repaymentScheduleDetail().isInterestRecalculationEnabled(),
|
|
||||||
isDisbursementMissed(), scheduleGeneratorDTO);
|
|
||||||
|
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
|
||||||
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
|
||||||
handleDisbursementTransaction(actualDisbursementDate, paymentDetail);
|
|
||||||
updateLoanSummaryDerivedFields();
|
|
||||||
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add an interest applied transaction of the interest is accrued upfront (Up front accrual), no accounting or
|
|
||||||
* cash based accounting is selected
|
|
||||||
*/
|
|
||||||
if (((isMultiDisburmentLoan() && getDisbursedLoanDisbursementDetails().size() == 1) || !isMultiDisburmentLoan())
|
|
||||||
&& isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
|
|
||||||
ExternalId externalId = ExternalId.empty();
|
|
||||||
if (TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
|
|
||||||
externalId = ExternalId.generate();
|
|
||||||
}
|
|
||||||
final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(getOffice(), this, interestApplied,
|
|
||||||
actualDisbursementDate, externalId);
|
|
||||||
addLoanTransaction(interestAppliedTransaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
ChangedTransactionDetail result = reprocessTransactionForDisbursement();
|
|
||||||
this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, this);
|
|
||||||
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(this.loanStatus));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(boolean interestRecalculationEnabledParam,
|
|
||||||
boolean disbursementMissedParam, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
|
||||||
LocalDate firstInstallmentDueDate = fetchRepaymentScheduleInstallment(1).getDueDate();
|
|
||||||
if ((interestRecalculationEnabledParam && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
|
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<LoanDisbursementDetails> getDisbursedLoanDisbursementDetails() {
|
|
||||||
return getDisbursementDetails().stream() //
|
return getDisbursementDetails().stream() //
|
||||||
.filter(it -> it.actualDisbursementDate() != null) //
|
.filter(it -> it.actualDisbursementDate() != null) //
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
|
|
||||||
final LocalDate actualDisbursementDate, BigDecimal emiAmount, LocalDate nextPossibleRepaymentDate,
|
|
||||||
LocalDate rescheduledRepaymentDate) {
|
|
||||||
boolean isEmiAmountChanged = false;
|
|
||||||
if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.isCanDefineInstallmentAmount()) && emiAmount != null
|
|
||||||
&& emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
|
||||||
if (this.loanProduct.isMultiDisburseLoan()) {
|
|
||||||
final LocalDate dateValue = null;
|
|
||||||
final boolean isSpecificToInstallment = false;
|
|
||||||
final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = scheduleGeneratorDTO
|
|
||||||
.isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
|
|
||||||
LocalDate effectiveDateFrom = actualDisbursementDate;
|
|
||||||
if (!isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled && actualDisbursementDate.equals(nextPossibleRepaymentDate)) {
|
|
||||||
effectiveDateFrom = nextPossibleRepaymentDate.plusDays(1);
|
|
||||||
}
|
|
||||||
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
|
||||||
effectiveDateFrom, emiAmount, dateValue, isSpecificToInstallment, this, LoanStatus.ACTIVE.getValue());
|
|
||||||
this.loanTermVariations.add(loanVariationTerms);
|
|
||||||
} else {
|
|
||||||
this.fixedEmiAmount = emiAmount;
|
|
||||||
}
|
|
||||||
isEmiAmountChanged = true;
|
|
||||||
}
|
|
||||||
if (rescheduledRepaymentDate != null && this.loanProduct.isMultiDisburseLoan()) {
|
|
||||||
final boolean isSpecificToInstallment = false;
|
|
||||||
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.DUE_DATE.getValue(),
|
|
||||||
nextPossibleRepaymentDate, emiAmount, rescheduledRepaymentDate, isSpecificToInstallment, this,
|
|
||||||
LoanStatus.ACTIVE.getValue());
|
|
||||||
this.loanTermVariations.add(loanVariationTerms);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged
|
|
||||||
|| rescheduledRepaymentDate != null) {
|
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
|
||||||
} else {
|
|
||||||
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canDisburse(final LocalDate actualDisbursementDate) {
|
public boolean canDisburse(final LocalDate actualDisbursementDate) {
|
||||||
LocalDate loanSubmittedOnDate = this.submittedOnDate;
|
LocalDate loanSubmittedOnDate = this.submittedOnDate;
|
||||||
final LoanStatus statusEnum = this.loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_DISBURSED, this);
|
final LoanStatus statusEnum = this.loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_DISBURSED, this);
|
||||||
@@ -1918,25 +1822,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChangedTransactionDetail reprocessTransactionForDisbursement() {
|
|
||||||
ChangedTransactionDetail changedTransactionDetail = null;
|
|
||||||
if (this.loanProduct.isMultiDisburseLoan()) {
|
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing();
|
|
||||||
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
|
||||||
.determineProcessor(this.transactionProcessingStrategyCode);
|
|
||||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(),
|
|
||||||
allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
|
||||||
mapEntry.getValue().updateLoan(this);
|
|
||||||
}
|
|
||||||
this.loanTransactions.addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
|
||||||
}
|
|
||||||
updateLoanSummaryDerivedFields();
|
|
||||||
}
|
|
||||||
return changedTransactionDetail;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Collection<LoanDisbursementDetails> fetchUndisbursedDetail() {
|
private Collection<LoanDisbursementDetails> fetchUndisbursedDetail() {
|
||||||
Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<>();
|
Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<>();
|
||||||
LocalDate date = null;
|
LocalDate date = null;
|
||||||
@@ -1993,15 +1878,19 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
|
|
||||||
private boolean isDisbursementAllowed() {
|
private boolean isDisbursementAllowed() {
|
||||||
List<LoanDisbursementDetails> disbursementDetails = getDisbursementDetails();
|
List<LoanDisbursementDetails> disbursementDetails = getDisbursementDetails();
|
||||||
return disbursementDetails == null || disbursementDetails.isEmpty()
|
boolean isSingleDisburseLoanDisbursementAllowed = disbursementDetails == null || disbursementDetails.isEmpty()
|
||||||
|| disbursementDetails.stream().anyMatch(it -> it.actualDisbursementDate() == null);
|
|| disbursementDetails.stream().anyMatch(it -> it.actualDisbursementDate() == null);
|
||||||
|
boolean isMultiDisburseLoanDisbursementAllowed = isMultiDisburmentLoan()
|
||||||
|
&& (disbursementDetails == null || disbursementDetails.stream().filter(it -> it.actualDisbursementDate() != null)
|
||||||
|
.count() < loanProduct.getLoanProductTrancheDetails().maxTrancheCount());
|
||||||
|
return isSingleDisburseLoanDisbursementAllowed || isMultiDisburseLoanDisbursementAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean atLeastOnceDisbursed() {
|
private boolean atLeastOnceDisbursed() {
|
||||||
return getDisbursementDetails().stream().anyMatch(it -> it.actualDisbursementDate() != null);
|
return getDisbursementDetails().stream().anyMatch(it -> it.actualDisbursementDate() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLoanRepaymentPeriodsDerivedFields(final LocalDate actualDisbursementDate) {
|
public void updateLoanRepaymentPeriodsDerivedFields(final LocalDate actualDisbursementDate) {
|
||||||
List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments();
|
List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments();
|
||||||
for (final LoanRepaymentScheduleInstallment repaymentPeriod : installments) {
|
for (final LoanRepaymentScheduleInstallment repaymentPeriod : installments) {
|
||||||
repaymentPeriod.updateDerivedFields(loanCurrency(), actualDisbursementDate);
|
repaymentPeriod.updateDerivedFields(loanCurrency(), actualDisbursementDate);
|
||||||
@@ -2075,7 +1964,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return interestRate;
|
return interestRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDisbursementTransaction(final LocalDate disbursedOn, final PaymentDetail paymentDetail) {
|
public void handleDisbursementTransaction(final LocalDate disbursedOn, final PaymentDetail paymentDetail) {
|
||||||
// add repayment transaction to track incoming money from client to mfi
|
// add repayment transaction to track incoming money from client to mfi
|
||||||
// for (charges due at time of disbursement)
|
// for (charges due at time of disbursement)
|
||||||
|
|
||||||
@@ -2099,11 +1988,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
* create a Charge applied transaction if Up front Accrual, None or Cash based accounting is enabled
|
* create a Charge applied transaction if Up front Accrual, None or Cash based accounting is enabled
|
||||||
*/
|
*/
|
||||||
if ((charge.getCharge().getChargeTimeType().equals(ChargeTimeType.DISBURSEMENT.getValue())
|
if ((charge.getCharge().getChargeTimeType().equals(ChargeTimeType.DISBURSEMENT.getValue())
|
||||||
&& disbursedOn.equals(actualDisbursementDate) && (actualDisbursementDate != null) && !charge.isWaived()
|
&& disbursedOn.equals(actualDisbursementDate) && !charge.isWaived() && !charge.isFullyPaid())
|
||||||
&& !charge.isFullyPaid())
|
|
||||||
|| (charge.getCharge().getChargeTimeType().equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue())
|
|| (charge.getCharge().getChargeTimeType().equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue())
|
||||||
&& disbursedOn.equals(actualDisbursementDate) && (actualDisbursementDate != null) && !charge.isWaived()
|
&& disbursedOn.equals(actualDisbursementDate) && !charge.isWaived() && !charge.isFullyPaid())) {
|
||||||
&& !charge.isFullyPaid())) {
|
|
||||||
if (totalFeeChargesDueAtDisbursement.isGreaterThanZero() && !charge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
if (totalFeeChargesDueAtDisbursement.isGreaterThanZero() && !charge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||||
charge.markAsFullyPaid();
|
charge.markAsFullyPaid();
|
||||||
// Add "Loan Charge Paid By" details to this transaction
|
// Add "Loan Charge Paid By" details to this transaction
|
||||||
@@ -2244,7 +2131,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(this.loanStatus));
|
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(this.loanStatus));
|
||||||
|
|
||||||
final LocalDate actualDisbursementDate = getDisbursementDate();
|
final LocalDate actualDisbursementDate = getDisbursementDate();
|
||||||
final boolean isScheduleRegenerateRequired = isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate);
|
final boolean isScheduleRegenerateRequired = isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate);
|
||||||
this.actualDisbursementDate = null;
|
this.actualDisbursementDate = null;
|
||||||
this.disbursedBy = null;
|
this.disbursedBy = null;
|
||||||
boolean isDisbursedAmountChanged = !MathUtil.isEqualTo(approvedPrincipal,
|
boolean isDisbursedAmountChanged = !MathUtil.isEqualTo(approvedPrincipal,
|
||||||
@@ -2461,7 +2348,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loanTransaction.isRecoveryRepayment()
|
if (loanTransaction.isRecoveryRepayment()
|
||||||
&& loanTransaction.getAmount(loanCurrency()).getAmount().compareTo(getLoanSummary().getTotalWrittenOff()) > 0) {
|
&& loanTransaction.getAmount(loanCurrency()).getAmount().compareTo(getSummary().getTotalWrittenOff()) > 0) {
|
||||||
final String errorMessage = "The transaction amount cannot greater than the remaining written off amount.";
|
final String errorMessage = "The transaction amount cannot greater than the remaining written off amount.";
|
||||||
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.greater.than.total.written.off", errorMessage);
|
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.greater.than.total.written.off", errorMessage);
|
||||||
}
|
}
|
||||||
@@ -3210,8 +3097,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
|
|
||||||
public boolean isAllTranchesNotDisbursed() {
|
public boolean isAllTranchesNotDisbursed() {
|
||||||
LoanStatus actualLoanStatus = LoanStatus.fromInt(this.loanStatus);
|
LoanStatus actualLoanStatus = LoanStatus.fromInt(this.loanStatus);
|
||||||
return this.loanProduct.isMultiDisburseLoan() && (actualLoanStatus.isActive() || actualLoanStatus.isApproved()
|
boolean isInRightStatus = actualLoanStatus.isActive() || actualLoanStatus.isApproved() || actualLoanStatus.isClosedObligationsMet()
|
||||||
|| actualLoanStatus.isClosedObligationsMet() || actualLoanStatus.isOverpaid()) && isDisbursementAllowed();
|
|| actualLoanStatus.isOverpaid();
|
||||||
|
return this.loanProduct.isMultiDisburseLoan() && isInRightStatus && isDisbursementAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasDisbursementTransaction() {
|
private boolean hasDisbursementTransaction() {
|
||||||
@@ -3263,7 +3151,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return principal;
|
return principal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) {
|
public boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) {
|
||||||
boolean isRegenerationRequired = false;
|
boolean isRegenerationRequired = false;
|
||||||
if (this.loanProduct.isMultiDisburseLoan()) {
|
if (this.loanProduct.isMultiDisburseLoan()) {
|
||||||
LoanDisbursementDetails details = fetchLastDisburseDetail();
|
LoanDisbursementDetails details = fetchLastDisburseDetail();
|
||||||
@@ -3274,10 +3162,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return isRegenerationRequired || !DateUtils.isEqual(actualDisbursedOnDate, this.expectedDisbursementDate);
|
return isRegenerationRequired || !DateUtils.isEqual(actualDisbursedOnDate, this.expectedDisbursementDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRepaymentScheduleRegenerationRequiredForDisbursement(final LocalDate actualDisbursementDate) {
|
|
||||||
return isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Money getTotalPaidInRepayments() {
|
private Money getTotalPaidInRepayments() {
|
||||||
Money cumulativePaid = Money.zero(loanCurrency());
|
Money cumulativePaid = Money.zero(loanCurrency());
|
||||||
|
|
||||||
@@ -3814,20 +3698,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return dueRepaymentPeriodDate.minusDays(1);// get 2n-1 range date from startDate
|
return dueRepaymentPeriodDate.minusDays(1);// get 2n-1 range date from startDate
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateDisbursementDateIsOnNonWorkingDay(final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) {
|
|
||||||
if (!allowTransactionsOnNonWorkingDay && !WorkingDaysUtil.isWorkingDay(workingDays, getDisbursementDate())) {
|
|
||||||
final String errorMessage = "Expected disbursement date cannot be on a non working day";
|
|
||||||
throw new LoanApplicationDateException("disbursement.date.on.non.working.day", errorMessage, getExpectedDisbursedOnLocalDate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateDisbursementDateIsOnHoliday(final boolean allowTransactionsOnHoliday, final List<Holiday> holidays) {
|
|
||||||
if (!allowTransactionsOnHoliday && HolidayUtil.isHoliday(getDisbursementDate(), holidays)) {
|
|
||||||
final String errorMessage = "Expected disbursement date cannot be on a holiday";
|
|
||||||
throw new LoanApplicationDateException("disbursement.date.on.holiday", errorMessage, getExpectedDisbursedOnLocalDate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validateRepaymentDateIsOnNonWorkingDay(final LocalDate repaymentDate, final WorkingDays workingDays,
|
public void validateRepaymentDateIsOnNonWorkingDay(final LocalDate repaymentDate, final WorkingDays workingDays,
|
||||||
final boolean allowTransactionsOnNonWorkingDay) {
|
final boolean allowTransactionsOnNonWorkingDay) {
|
||||||
if (!allowTransactionsOnNonWorkingDay && !WorkingDaysUtil.isWorkingDay(workingDays, repaymentDate)) {
|
if (!allowTransactionsOnNonWorkingDay && !WorkingDaysUtil.isWorkingDay(workingDays, repaymentDate)) {
|
||||||
@@ -4838,13 +4708,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
|
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Loan summary embedded object
|
|
||||||
**/
|
|
||||||
public LoanSummary getLoanSummary() {
|
|
||||||
return this.summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateRescheduledByUser(AppUser rescheduledByUser) {
|
public void updateRescheduledByUser(AppUser rescheduledByUser) {
|
||||||
this.rescheduledByUser = rescheduledByUser;
|
this.rescheduledByUser = rescheduledByUser;
|
||||||
}
|
}
|
||||||
@@ -5245,7 +5108,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
|
|
||||||
public LoanRepaymentScheduleInstallment fetchLoanForeclosureDetail(final LocalDate closureDate) {
|
public LoanRepaymentScheduleInstallment fetchLoanForeclosureDetail(final LocalDate closureDate) {
|
||||||
Money[] receivables = retriveIncomeOutstandingTillDate(closureDate);
|
Money[] receivables = retriveIncomeOutstandingTillDate(closureDate);
|
||||||
Money totalPrincipal = Money.of(getCurrency(), this.getLoanSummary().getTotalPrincipalOutstanding());
|
Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding());
|
||||||
totalPrincipal = totalPrincipal.minus(receivables[3]);
|
totalPrincipal = totalPrincipal.minus(receivables[3]);
|
||||||
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
|
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
|
||||||
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||||
|
|||||||
+18
-18
@@ -262,25 +262,25 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate,
|
public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate,
|
||||||
final ExternalId externalId) {
|
final ExternalId externalId) {
|
||||||
return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate,
|
return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate,
|
||||||
loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(),
|
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
|
||||||
loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(),
|
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
|
||||||
loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
|
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate,
|
public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate,
|
||||||
final ExternalId externalId) {
|
final ExternalId externalId) {
|
||||||
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate,
|
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate,
|
||||||
loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(),
|
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
|
||||||
loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(),
|
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
|
||||||
loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
|
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate,
|
public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate,
|
||||||
final ExternalId externalId) {
|
final ExternalId externalId) {
|
||||||
return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate,
|
return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate,
|
||||||
loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(),
|
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
|
||||||
loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(),
|
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
|
||||||
loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
|
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction refund(final Office office, final Money amount, final PaymentDetail paymentDetail,
|
public static LoanTransaction refund(final Office office, final Money amount, final PaymentDetail paymentDetail,
|
||||||
@@ -369,19 +369,19 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction chargeOff(final Loan loan, final LocalDate chargeOffDate, final ExternalId externalId) {
|
public static LoanTransaction chargeOff(final Loan loan, final LocalDate chargeOffDate, final ExternalId externalId) {
|
||||||
BigDecimal principalPortion = loan.getLoanSummary().getTotalPrincipalOutstanding().compareTo(BigDecimal.ZERO) != 0
|
BigDecimal principalPortion = loan.getSummary().getTotalPrincipalOutstanding().compareTo(BigDecimal.ZERO) != 0
|
||||||
? loan.getLoanSummary().getTotalPrincipalOutstanding()
|
? loan.getSummary().getTotalPrincipalOutstanding()
|
||||||
: null;
|
: null;
|
||||||
BigDecimal interestPortion = loan.getLoanSummary().getTotalInterestOutstanding().compareTo(BigDecimal.ZERO) != 0
|
BigDecimal interestPortion = loan.getSummary().getTotalInterestOutstanding().compareTo(BigDecimal.ZERO) != 0
|
||||||
? loan.getLoanSummary().getTotalInterestOutstanding()
|
? loan.getSummary().getTotalInterestOutstanding()
|
||||||
: null;
|
: null;
|
||||||
BigDecimal feePortion = loan.getLoanSummary().getTotalFeeChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
|
BigDecimal feePortion = loan.getSummary().getTotalFeeChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
|
||||||
? loan.getLoanSummary().getTotalFeeChargesOutstanding()
|
? loan.getSummary().getTotalFeeChargesOutstanding()
|
||||||
: null;
|
: null;
|
||||||
BigDecimal penaltyPortion = loan.getLoanSummary().getTotalPenaltyChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
|
BigDecimal penaltyPortion = loan.getSummary().getTotalPenaltyChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
|
||||||
? loan.getLoanSummary().getTotalPenaltyChargesOutstanding()
|
? loan.getSummary().getTotalPenaltyChargesOutstanding()
|
||||||
: null;
|
: null;
|
||||||
BigDecimal totalOutstanding = loan.getLoanSummary().getTotalOutstanding();
|
BigDecimal totalOutstanding = loan.getSummary().getTotalOutstanding();
|
||||||
|
|
||||||
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CHARGE_OFF.getValue(), chargeOffDate, totalOutstanding,
|
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CHARGE_OFF.getValue(), chargeOffDate, totalOutstanding,
|
||||||
principalPortion, interestPortion, feePortion, penaltyPortion, null, false, null, externalId);
|
principalPortion, interestPortion, feePortion, penaltyPortion, null, false, null, externalId);
|
||||||
|
|||||||
+1
-1
@@ -2494,7 +2494,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
|
|||||||
currency, applyInterestRecalculation);
|
currency, applyInterestRecalculation);
|
||||||
retainedInstallments.addAll(newRepaymentScheduleInstallments);
|
retainedInstallments.addAll(newRepaymentScheduleInstallments);
|
||||||
loanScheduleParams.getCompoundingDateVariations().putAll(compoundingDateVariations);
|
loanScheduleParams.getCompoundingDateVariations().putAll(compoundingDateVariations);
|
||||||
loanApplicationTerms.updateTotalInterestDue(Money.of(currency, loan.getLoanSummary().getTotalInterestCharged()));
|
loanApplicationTerms.updateTotalInterestDue(Money.of(currency, loan.getSummary().getTotalInterestCharged()));
|
||||||
} else {
|
} else {
|
||||||
loanApplicationTerms.getLoanTermVariations().resetVariations();
|
loanApplicationTerms.getLoanTermVariations().resetVariations();
|
||||||
periods.clear();
|
periods.clear();
|
||||||
|
|||||||
+1
-1
@@ -105,7 +105,7 @@ public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService {
|
|||||||
Map<Long, List<LoanSchedulePeriodData>> scheduleDate = this.jdbcTemplate.query(originalScheduleExtractor.schema,
|
Map<Long, List<LoanSchedulePeriodData>> scheduleDate = this.jdbcTemplate.query(originalScheduleExtractor.schema,
|
||||||
originalScheduleExtractor);
|
originalScheduleExtractor);
|
||||||
if (scheduleDate.size() > 0) {
|
if (scheduleDate.size() > 0) {
|
||||||
List<Map<String, Object>> transactions = getLoanSummary(loan.getId(), loan.getLoanSummary());
|
List<Map<String, Object>> transactions = getLoanSummary(loan.getId(), loan.getSummary());
|
||||||
updateScheduleWithPaidDetail(scheduleDate, transactions);
|
updateScheduleWithPaidDetail(scheduleDate, transactions);
|
||||||
createInsertStatements(updateStatement, scheduleDate, count == 0);
|
createInsertStatements(updateStatement, scheduleDate, count == 0);
|
||||||
if (updateStatement.size() == 1) {
|
if (updateStatement.size() == 1) {
|
||||||
|
|||||||
+1
-1
@@ -80,7 +80,7 @@ public class CheckLoanRepaymentDueBusinessStep implements LoanCOBBusinessStep {
|
|||||||
LoanRepaymentScheduleInstallment repaymentScheduleInstallment, LocalDate repaymentDate, List<LoanStatus> nonDisbursedStatuses) {
|
LoanRepaymentScheduleInstallment repaymentScheduleInstallment, LocalDate repaymentDate, List<LoanStatus> nonDisbursedStatuses) {
|
||||||
return repaymentDate.minusDays(numberOfDaysBeforeDueDateToRaiseEvent).equals(currentDate)
|
return repaymentDate.minusDays(numberOfDaysBeforeDueDateToRaiseEvent).equals(currentDate)
|
||||||
&& !nonDisbursedStatuses.contains(loan.getStatus())
|
&& !nonDisbursedStatuses.contains(loan.getStatus())
|
||||||
&& loan.getLoanSummary().getTotalOutstanding().compareTo(BigDecimal.ZERO) > 0
|
&& loan.getSummary().getTotalOutstanding().compareTo(BigDecimal.ZERO) > 0
|
||||||
&& repaymentScheduleInstallment.getTotalOutstanding(loan.getCurrency()).isGreaterThanZero();
|
&& repaymentScheduleInstallment.getTotalOutstanding(loan.getCurrency()).isGreaterThanZero();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -45,8 +45,7 @@ public class CheckLoanRepaymentOverdueBusinessStep implements LoanCOBBusinessSte
|
|||||||
public Loan execute(Loan loan) {
|
public Loan execute(Loan loan) {
|
||||||
List<LoanStatus> nonDisbursedStatuses = Arrays.asList(LoanStatus.INVALID, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL,
|
List<LoanStatus> nonDisbursedStatuses = Arrays.asList(LoanStatus.INVALID, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL,
|
||||||
LoanStatus.APPROVED);
|
LoanStatus.APPROVED);
|
||||||
if (!nonDisbursedStatuses.contains(loan.getStatus())
|
if (!nonDisbursedStatuses.contains(loan.getStatus()) && loan.getSummary().getTotalOutstanding().compareTo(BigDecimal.ZERO) > 0) {
|
||||||
&& loan.getLoanSummary().getTotalOutstanding().compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
log.debug("start processing loan repayment overdue business step for loan with Id [{}]", loan.getId());
|
log.debug("start processing loan repayment overdue business step for loan with Id [{}]", loan.getId());
|
||||||
Long numberOfDaysAfterDueDateToRaiseEvent = configurationDomainService.retrieveRepaymentOverdueDays();
|
Long numberOfDaysAfterDueDateToRaiseEvent = configurationDomainService.retrieveRepaymentOverdueDays();
|
||||||
if (loan.getLoanProduct().getOverDueDaysForRepaymentEvent() != null) {
|
if (loan.getLoanProduct().getOverDueDaysForRepaymentEvent() != null) {
|
||||||
|
|||||||
+2
-2
@@ -97,8 +97,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
|
|
||||||
if (loan.loanProduct().isMultiDisburseLoan()) {
|
if (loan.loanProduct().isMultiDisburseLoan()) {
|
||||||
BigDecimal disbursedAmount = loan.getDisbursedAmount();
|
BigDecimal disbursedAmount = loan.getDisbursedAmount();
|
||||||
BigDecimal principalRepaid = loan.getLoanSummary().getTotalPrincipalRepaid();
|
BigDecimal principalRepaid = loan.getSummary().getTotalPrincipalRepaid();
|
||||||
BigDecimal principalWrittenOff = loan.getLoanSummary().getTotalPrincipalWrittenOff();
|
BigDecimal principalWrittenOff = loan.getSummary().getTotalPrincipalWrittenOff();
|
||||||
if (disbursedAmount.subtract(principalWrittenOff).subtract(principalRepaid).compareTo(BigDecimal.ZERO) <= 0) {
|
if (disbursedAmount.subtract(principalWrittenOff).subtract(principalRepaid).compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+136
-35
@@ -38,11 +38,15 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||||
|
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
|
||||||
|
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
|
||||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||||
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
|
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
|
||||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||||
@@ -52,26 +56,35 @@ import org.apache.fineract.organisation.monetary.exception.CurrencyNotFoundExcep
|
|||||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||||
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
|
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
|
||||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||||
|
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
|
||||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||||
|
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||||
import org.apache.fineract.portfolio.calendar.exception.NotValidRecurringDateException;
|
import org.apache.fineract.portfolio.calendar.exception.NotValidRecurringDateException;
|
||||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
||||||
|
import org.apache.fineract.portfolio.collateralmanagement.exception.LoanCollateralAmountNotSufficientException;
|
||||||
import org.apache.fineract.portfolio.group.domain.Group;
|
import org.apache.fineract.portfolio.group.domain.Group;
|
||||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanChargeRefundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanChargeRefundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanRepaymentScheduleNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanRepaymentScheduleNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@@ -81,8 +94,11 @@ public final class LoanTransactionValidator {
|
|||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
private final LoanApplicationValidator fromApiJsonDeserializer;
|
private final LoanApplicationValidator fromApiJsonDeserializer;
|
||||||
private final LoanRepository loanRepository;
|
private final LoanRepository loanRepository;
|
||||||
|
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||||
private final ApplicationCurrencyRepository applicationCurrencyRepository;
|
private final ApplicationCurrencyRepository applicationCurrencyRepository;
|
||||||
private final LoanUtilService loanUtilService;
|
private final LoanUtilService loanUtilService;
|
||||||
|
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
|
||||||
|
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||||
|
|
||||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||||
if (!dataValidationErrors.isEmpty()) {
|
if (!dataValidationErrors.isEmpty()) {
|
||||||
@@ -91,13 +107,129 @@ public final class LoanTransactionValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateDisbursement(final String json, boolean isAccountTransfer) {
|
public void validateDisbursement(JsonCommand command, boolean isAccountTransfer, Long loanId) {
|
||||||
|
String json = command.json();
|
||||||
if (StringUtils.isBlank(json)) {
|
if (StringUtils.isBlank(json)) {
|
||||||
throw new InvalidJsonException();
|
throw new InvalidJsonException();
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> disbursementParameters = null;
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, getDisbursementParameters(isAccountTransfer));
|
||||||
|
|
||||||
|
LoanApplicationValidator.validateOrThrow("loan.disbursement", baseDataValidator -> {
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
final LocalDate actualDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("actualDisbursementDate", element);
|
||||||
|
baseDataValidator.reset().parameter("actualDisbursementDate").value(actualDisbursementDate).notNull();
|
||||||
|
|
||||||
|
final String note = this.fromApiJsonHelper.extractStringNamed("note", element);
|
||||||
|
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
|
||||||
|
|
||||||
|
final BigDecimal principal = this.fromApiJsonHelper
|
||||||
|
.extractBigDecimalWithLocaleNamed(LoanApiConstants.principalDisbursedParameterName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanApiConstants.principalDisbursedParameterName).value(principal).ignoreIfNull()
|
||||||
|
.positiveAmount();
|
||||||
|
|
||||||
|
final BigDecimal netDisbursalAmount = this.fromApiJsonHelper
|
||||||
|
.extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanApiConstants.disbursementNetDisbursalAmountParameterName).value(netDisbursalAmount)
|
||||||
|
.ignoreIfNull().positiveAmount();
|
||||||
|
|
||||||
|
final BigDecimal emiAmount = this.fromApiJsonHelper
|
||||||
|
.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedEmiAmountParameterName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanApiConstants.fixedEmiAmountParameterName).value(emiAmount).ignoreIfNull()
|
||||||
|
.positiveAmount().notGreaterThanMax(principal);
|
||||||
|
|
||||||
|
validatePaymentDetails(baseDataValidator, element);
|
||||||
|
|
||||||
|
if (command.parameterExists("postDatedChecks")) {
|
||||||
|
this.validateDisbursementWithPostDatedChecks(command.json(), loanId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||||
|
validateLoanClientIsActive(loan);
|
||||||
|
validateLoanGroupIsActive(loan);
|
||||||
|
|
||||||
|
if (loan.isChargedOff() && DateUtils.isBefore(actualDisbursementDate, loan.getChargedOffOnDate())) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
|
||||||
|
+ loan.getId()
|
||||||
|
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
|
||||||
|
loan.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isSingleDisburseLoan = !loan.getLoanProduct().isMultiDisburseLoan();
|
||||||
|
boolean isSingleDisburseNotApprovedOrDisbursedAlready = isSingleDisburseLoan && !(loan.isApproved() && loan.isNotDisbursed());
|
||||||
|
boolean isMultiDisburseLoanAndAllTranchesDisbursed = loan.getLoanProduct().isMultiDisburseLoan()
|
||||||
|
&& !loan.isAllTranchesNotDisbursed();
|
||||||
|
if (isSingleDisburseNotApprovedOrDisbursedAlready || isMultiDisburseLoanAndAllTranchesDisbursed) {
|
||||||
|
final String defaultUserMessage = "Loan Disbursal is not allowed. Loan Account is not in approved and not disbursed state.";
|
||||||
|
final ApiParameterError error = ApiParameterError
|
||||||
|
.generalError("error.msg.loan.disbursal.account.is.not.approve.not.disbursed.state", defaultUserMessage);
|
||||||
|
baseDataValidator.getDataValidationErrors().add(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
final BigDecimal disbursedAmount = loan.getDisbursedAmount();
|
||||||
|
final Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
|
||||||
|
|
||||||
|
if ((loanCollateralManagements != null && !loanCollateralManagements.isEmpty()) && loan.getLoanType().isIndividualAccount()) {
|
||||||
|
BigDecimal totalCollateral = collectTotalCollateral(loanCollateralManagements);
|
||||||
|
|
||||||
|
// Validate the loan collateral value against the disbursedAmount
|
||||||
|
if (disbursedAmount.compareTo(totalCollateral) > 0) {
|
||||||
|
throw new LoanCollateralAmountNotSufficientException(disbursedAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate ActualDisbursement Date Against Expected Disbursement Date
|
||||||
|
LoanProduct loanProduct = loan.loanProduct();
|
||||||
|
if (loanProduct.isSyncExpectedWithDisbursementDate()) {
|
||||||
|
if (!loan.getExpectedDisbursedOnLocalDate().equals(actualDisbursementDate)) {
|
||||||
|
throw new DateMismatchException(actualDisbursementDate, loan.getExpectedDisbursedOnLocalDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entityDatatableChecksWritePlatformService.runTheCheckForProduct(loan.getId(), EntityTables.LOAN.getName(),
|
||||||
|
StatusEnum.DISBURSE.getValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
|
||||||
|
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
|
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||||
|
CalendarEntityType.LOANS.getValue());
|
||||||
|
if (loan.isSyncDisbursementWithMeeting()) {
|
||||||
|
validateDisbursementDateWithMeetingDate(actualDisbursementDate, calendarInstance,
|
||||||
|
scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), scheduleGeneratorDTO.getNumberOfdays());
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate if disbursement date is a holiday or a non-working day
|
||||||
|
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||||
|
if (!holidayDetailDTO.isAllowTransactionsOnNonWorkingDay()
|
||||||
|
&& !WorkingDaysUtil.isWorkingDay(holidayDetailDTO.getWorkingDays(), loan.getDisbursementDate())) {
|
||||||
|
final String errorMessage = "Expected disbursement date cannot be on a non working day";
|
||||||
|
throw new LoanApplicationDateException("disbursement.date.on.non.working.day", errorMessage,
|
||||||
|
loan.getExpectedDisbursedOnLocalDate());
|
||||||
|
}
|
||||||
|
if (!holidayDetailDTO.isAllowTransactionsOnHoliday()
|
||||||
|
&& HolidayUtil.isHoliday(loan.getDisbursementDate(), holidayDetailDTO.getHolidays())) {
|
||||||
|
final String errorMessage = "Expected disbursement date cannot be on a holiday";
|
||||||
|
throw new LoanApplicationDateException("disbursement.date.on.holiday", errorMessage,
|
||||||
|
loan.getExpectedDisbursedOnLocalDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull BigDecimal collectTotalCollateral(Set<LoanCollateralManagement> loanCollateralManagements) {
|
||||||
|
BigDecimal totalCollateral = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
|
||||||
|
BigDecimal quantity = loanCollateralManagement.getQuantity();
|
||||||
|
BigDecimal pctToBase = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getPctToBase();
|
||||||
|
BigDecimal basePrice = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getBasePrice();
|
||||||
|
totalCollateral = totalCollateral.add(quantity.multiply(basePrice).multiply(pctToBase).divide(BigDecimal.valueOf(100)));
|
||||||
|
}
|
||||||
|
return totalCollateral;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull Set<String> getDisbursementParameters(boolean isAccountTransfer) {
|
||||||
|
Set<String> disbursementParameters;
|
||||||
|
|
||||||
if (isAccountTransfer) {
|
if (isAccountTransfer) {
|
||||||
disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale", "dateFormat",
|
disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale", "dateFormat",
|
||||||
@@ -109,38 +241,7 @@ public final class LoanTransactionValidator {
|
|||||||
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.fixedEmiAmountParameterName,
|
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.fixedEmiAmountParameterName,
|
||||||
LoanApiConstants.postDatedChecks, LoanApiConstants.disbursementNetDisbursalAmountParameterName));
|
LoanApiConstants.postDatedChecks, LoanApiConstants.disbursementNetDisbursalAmountParameterName));
|
||||||
}
|
}
|
||||||
|
return disbursementParameters;
|
||||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
|
||||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
|
||||||
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.disbursement");
|
|
||||||
|
|
||||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
|
||||||
final LocalDate actualDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("actualDisbursementDate", element);
|
|
||||||
baseDataValidator.reset().parameter("actualDisbursementDate").value(actualDisbursementDate).notNull();
|
|
||||||
|
|
||||||
final String note = this.fromApiJsonHelper.extractStringNamed("note", element);
|
|
||||||
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
|
|
||||||
|
|
||||||
final BigDecimal principal = this.fromApiJsonHelper
|
|
||||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.principalDisbursedParameterName, element);
|
|
||||||
baseDataValidator.reset().parameter(LoanApiConstants.principalDisbursedParameterName).value(principal).ignoreIfNull()
|
|
||||||
.positiveAmount();
|
|
||||||
|
|
||||||
final BigDecimal netDisbursalAmount = this.fromApiJsonHelper
|
|
||||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName, element);
|
|
||||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementNetDisbursalAmountParameterName).value(netDisbursalAmount)
|
|
||||||
.ignoreIfNull().positiveAmount();
|
|
||||||
|
|
||||||
final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedEmiAmountParameterName,
|
|
||||||
element);
|
|
||||||
baseDataValidator.reset().parameter(LoanApiConstants.fixedEmiAmountParameterName).value(emiAmount).ignoreIfNull().positiveAmount()
|
|
||||||
.notGreaterThanMax(principal);
|
|
||||||
|
|
||||||
validatePaymentDetails(baseDataValidator, element);
|
|
||||||
|
|
||||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateDisbursementWithPostDatedChecks(final String json, final Long loanId) {
|
public void validateDisbursementWithPostDatedChecks(final String json, final Long loanId) {
|
||||||
|
|||||||
+134
-99
@@ -46,6 +46,7 @@ import org.apache.fineract.cob.service.LoanAccountLockService;
|
|||||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
|
import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigurationServiceContainer;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
@@ -135,7 +136,6 @@ import org.apache.fineract.portfolio.calendar.exception.CalendarParameterUpdateN
|
|||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
||||||
import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
|
import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
|
||||||
import org.apache.fineract.portfolio.collateralmanagement.exception.LoanCollateralAmountNotSufficientException;
|
|
||||||
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
||||||
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
|
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
|
||||||
import org.apache.fineract.portfolio.collectionsheet.command.SingleDisbursalCommand;
|
import org.apache.fineract.portfolio.collectionsheet.command.SingleDisbursalCommand;
|
||||||
@@ -164,6 +164,8 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
|
||||||
@@ -194,6 +196,7 @@ import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionVa
|
|||||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||||
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||||
import org.apache.fineract.portfolio.note.domain.Note;
|
import org.apache.fineract.portfolio.note.domain.Note;
|
||||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
@@ -213,6 +216,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
|
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
|
||||||
|
|
||||||
|
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final LoanTransactionValidator loanTransactionValidator;
|
private final LoanTransactionValidator loanTransactionValidator;
|
||||||
private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
|
private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
|
||||||
@@ -251,7 +255,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
private final RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler;
|
private final RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler;
|
||||||
private final PostDatedChecksRepository postDatedChecksRepository;
|
private final PostDatedChecksRepository postDatedChecksRepository;
|
||||||
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
|
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
|
||||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
private final LoanAccountLockService loanAccountLockService;
|
private final LoanAccountLockService loanAccountLockService;
|
||||||
private final ExternalIdFactory externalIdFactory;
|
private final ExternalIdFactory externalIdFactory;
|
||||||
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
||||||
@@ -294,28 +298,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand command, Boolean isAccountTransfer,
|
public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand command, Boolean isAccountTransfer,
|
||||||
Boolean isWithoutAutoPayment) {
|
Boolean isWithoutAutoPayment) {
|
||||||
|
loanTransactionValidator.validateDisbursement(command, isAccountTransfer, loanId);
|
||||||
|
|
||||||
final AppUser currentUser = getAppUserIfPresent();
|
Loan loan = loanAssembler.assembleFrom(loanId);
|
||||||
|
|
||||||
this.loanTransactionValidator.validateDisbursement(command.json(), isAccountTransfer);
|
|
||||||
|
|
||||||
if (command.parameterExists("postDatedChecks")) {
|
|
||||||
// validate with post dated checks for the disbursement
|
|
||||||
this.loanTransactionValidator.validateDisbursementWithPostDatedChecks(command.json(), loanId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Loan loan = this.loanAssembler.assembleFrom(loanId);
|
|
||||||
// Fail fast if client/group is not active or actual loan status disallows disbursal
|
|
||||||
checkClientOrGroupActive(loan);
|
|
||||||
|
|
||||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
|
||||||
|
|
||||||
if (loan.isChargedOff() && DateUtils.isBefore(actualDisbursementDate, loan.getChargedOffOnDate())) {
|
|
||||||
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
|
|
||||||
+ loanId
|
|
||||||
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
|
|
||||||
loanId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loan.loanProduct().isDisallowExpectedDisbursements()) {
|
if (loan.loanProduct().isDisallowExpectedDisbursements()) {
|
||||||
List<LoanDisbursementDetails> filteredList = loan.getDisbursementDetails().stream()
|
List<LoanDisbursementDetails> filteredList = loan.getDisbursementDetails().stream()
|
||||||
@@ -323,8 +308,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
// Check whether a new LoanDisbursementDetails is required
|
// Check whether a new LoanDisbursementDetails is required
|
||||||
if (filteredList.isEmpty()) {
|
if (filteredList.isEmpty()) {
|
||||||
// create artificial 'tranche/expected disbursal' as current disburse code expects it for
|
// create artificial 'tranche/expected disbursal' as current disburse code expects it for
|
||||||
// multi-disbursal
|
// multi-disbursal products
|
||||||
// products
|
|
||||||
final LocalDate artificialExpectedDate = loan.getExpectedDisbursedOnLocalDate();
|
final LocalDate artificialExpectedDate = loan.getExpectedDisbursedOnLocalDate();
|
||||||
LoanDisbursementDetails disbursementDetail = new LoanDisbursementDetails(artificialExpectedDate, null,
|
LoanDisbursementDetails disbursementDetail = new LoanDisbursementDetails(artificialExpectedDate, null,
|
||||||
loan.getDisbursedAmount(), null, false);
|
loan.getDisbursedAmount(), null, false);
|
||||||
@@ -332,61 +316,23 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
loan.getAllDisbursementDetails().add(disbursementDetail);
|
loan.getAllDisbursementDetails().add(disbursementDetail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loan.validateAccountStatus(LoanEvent.LOAN_DISBURSED);
|
|
||||||
|
|
||||||
// Get disbursedAmount
|
|
||||||
final BigDecimal disbursedAmount = loan.getDisbursedAmount();
|
|
||||||
final Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
|
|
||||||
|
|
||||||
// Get relevant loan collateral modules
|
|
||||||
if ((loanCollateralManagements != null && !loanCollateralManagements.isEmpty()) && loan.getLoanType().isIndividualAccount()) {
|
|
||||||
|
|
||||||
BigDecimal totalCollateral = BigDecimal.valueOf(0);
|
|
||||||
|
|
||||||
for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
|
|
||||||
BigDecimal quantity = loanCollateralManagement.getQuantity();
|
|
||||||
BigDecimal pctToBase = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getPctToBase();
|
|
||||||
BigDecimal basePrice = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getBasePrice();
|
|
||||||
totalCollateral = totalCollateral.add(quantity.multiply(basePrice).multiply(pctToBase).divide(BigDecimal.valueOf(100)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the loan collateral value against the disbursedAmount
|
|
||||||
if (disbursedAmount.compareTo(totalCollateral) > 0) {
|
|
||||||
throw new LoanCollateralAmountNotSufficientException(disbursedAmount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate ActualDisbursement Date Against Expected Disbursement Date
|
|
||||||
LoanProduct loanProduct = loan.loanProduct();
|
|
||||||
if (loanProduct.isSyncExpectedWithDisbursementDate()) {
|
|
||||||
syncExpectedDateWithActualDisbursementDate(loan, actualDisbursementDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
final LocalDate nextPossibleRepaymentDate = loan.getNextPossibleRepaymentDateForRescheduling();
|
final LocalDate nextPossibleRepaymentDate = loan.getNextPossibleRepaymentDateForRescheduling();
|
||||||
final LocalDate rescheduledRepaymentDate = command.localDateValueOfParameterNamed("adjustRepaymentDate");
|
final LocalDate rescheduledRepaymentDate = command.localDateValueOfParameterNamed("adjustRepaymentDate");
|
||||||
|
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, EntityTables.LOAN.getName(), StatusEnum.DISBURSE.getValue(),
|
|
||||||
EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
|
|
||||||
|
|
||||||
LocalDate recalculateFrom = null;
|
|
||||||
if (!loan.isMultiDisburmentLoan()) {
|
if (!loan.isMultiDisburmentLoan()) {
|
||||||
loan.setActualDisbursementDate(actualDisbursementDate);
|
loan.setActualDisbursementDate(actualDisbursementDate);
|
||||||
}
|
}
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
|
||||||
|
|
||||||
// validate actual disbursement date against meeting date
|
// validate actual disbursement date against meeting date
|
||||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
CalendarEntityType.LOANS.getValue());
|
|
||||||
if (loan.isSyncDisbursementWithMeeting()) {
|
|
||||||
this.loanTransactionValidator.validateDisbursementDateWithMeetingDate(actualDisbursementDate, calendarInstance,
|
|
||||||
scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), scheduleGeneratorDTO.getNumberOfdays());
|
|
||||||
}
|
|
||||||
|
|
||||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanDisbursalBusinessEvent(loan));
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanDisbursalBusinessEvent(loan));
|
||||||
|
|
||||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||||
|
|
||||||
|
final AppUser currentUser = getAppUserIfPresent();
|
||||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
|
|
||||||
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
||||||
@@ -400,12 +346,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
// Recalculate first repayment date based in actual disbursement date.
|
// Recalculate first repayment date based in actual disbursement date.
|
||||||
updateLoanCounters(loan, actualDisbursementDate);
|
updateLoanCounters(loan, actualDisbursementDate);
|
||||||
Money amountBeforeAdjust = loan.getPrincipal();
|
Money amountBeforeAdjust = loan.getPrincipal();
|
||||||
boolean canDisburse = loan.canDisburse(actualDisbursementDate);
|
|
||||||
ChangedTransactionDetail changedTransactionDetail = null;
|
|
||||||
final Locale locale = command.extractLocale();
|
final Locale locale = command.extractLocale();
|
||||||
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
|
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
|
||||||
if (canDisburse) {
|
|
||||||
|
|
||||||
|
if (loan.canDisburse(actualDisbursementDate)) {
|
||||||
// Get netDisbursalAmount from disbursal screen field.
|
// Get netDisbursalAmount from disbursal screen field.
|
||||||
final BigDecimal netDisbursalAmount = command
|
final BigDecimal netDisbursalAmount = command
|
||||||
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
|
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
|
||||||
@@ -441,9 +385,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
amountToDisburse = disburseAmount.minus(loanOutstanding);
|
amountToDisburse = disburseAmount.minus(loanOutstanding);
|
||||||
|
|
||||||
disburseLoanToLoan(loan, command, loanOutstanding);
|
disburseLoanToLoan(loan, command, loanOutstanding);
|
||||||
}
|
}
|
||||||
|
|
||||||
LoanTransaction disbursementTransaction = null;
|
LoanTransaction disbursementTransaction = null;
|
||||||
if (isAccountTransfer) {
|
if (isAccountTransfer) {
|
||||||
disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
|
disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
|
||||||
@@ -463,17 +407,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
*/
|
*/
|
||||||
recalculateSchedule = true;
|
recalculateSchedule = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate,
|
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate,
|
||||||
rescheduledRepaymentDate);
|
rescheduledRepaymentDate);
|
||||||
boolean downPaymentEnabled = loan.repaymentScheduleDetail().isEnableDownPayment();
|
boolean downPaymentEnabled = loan.repaymentScheduleDetail().isEnableDownPayment();
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || downPaymentEnabled) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || downPaymentEnabled) {
|
||||||
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
if (isPaymentTypeApplicableForDisbursementCharge) {
|
ChangedTransactionDetail changedTransactionDetail = disburseLoan(command, isPaymentTypeApplicableForDisbursementCharge,
|
||||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail);
|
paymentDetail, loan, currentUser, changes, scheduleGeneratorDTO);
|
||||||
} else {
|
|
||||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null);
|
|
||||||
}
|
|
||||||
loan.adjustNetDisbursalAmount(amountToDisburse.getAmount());
|
loan.adjustNetDisbursalAmount(amountToDisburse.getAmount());
|
||||||
|
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
@@ -612,6 +554,69 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ChangedTransactionDetail disburseLoan(JsonCommand command, boolean isPaymentTypeApplicableForDisbursementCharge,
|
||||||
|
PaymentDetail paymentDetail, Loan loan, AppUser currentUser, Map<String, Object> changes,
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
|
final PaymentDetail paymentDetail1 = isPaymentTypeApplicableForDisbursementCharge ? paymentDetail : null;
|
||||||
|
final LocalDate actualDisbursementDate1 = command.localDateValueOfParameterNamed(Loan.ACTUAL_DISBURSEMENT_DATE);
|
||||||
|
|
||||||
|
loan.setDisbursedBy(currentUser);
|
||||||
|
loan.updateLoanScheduleDependentDerivedFields();
|
||||||
|
|
||||||
|
changes.put(Loan.LOCALE, command.locale());
|
||||||
|
changes.put(Loan.DATE_FORMAT, command.dateFormat());
|
||||||
|
changes.put(Loan.ACTUAL_DISBURSEMENT_DATE, command.stringValueOfParameterNamed(Loan.ACTUAL_DISBURSEMENT_DATE));
|
||||||
|
|
||||||
|
boolean disbursementMissedParam = loan.isDisbursementMissed();
|
||||||
|
LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate();
|
||||||
|
if ((loan.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||||
|
&& (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
|
||||||
|
loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
|
loan.updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate1);
|
||||||
|
loan.handleDisbursementTransaction(actualDisbursementDate1, paymentDetail1);
|
||||||
|
loan.updateLoanSummaryDerivedFields();
|
||||||
|
final Money interestApplied = Money.of(loan.getCurrency(), loan.getSummary().getTotalInterestCharged());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add an interest applied transaction of the interest is accrued upfront (Up front accrual), no accounting or
|
||||||
|
* cash based accounting is selected
|
||||||
|
*/
|
||||||
|
if (((loan.isMultiDisburmentLoan() && loan.getDisbursedLoanDisbursementDetails().size() == 1) || !loan.isMultiDisburmentLoan())
|
||||||
|
&& loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
|
||||||
|
ExternalId externalId = ExternalId.empty();
|
||||||
|
if (TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
|
||||||
|
externalId = ExternalId.generate();
|
||||||
|
}
|
||||||
|
final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(loan.getOffice(), loan, interestApplied,
|
||||||
|
actualDisbursementDate1, externalId);
|
||||||
|
loan.addLoanTransaction(interestAppliedTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangedTransactionDetail changedTransactionDetail = null;
|
||||||
|
if (loan.getLoanProduct().isMultiDisburseLoan()) {
|
||||||
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = loan.retrieveListOfTransactionsForReprocessing();
|
||||||
|
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = transactionProcessorFactory
|
||||||
|
.determineProcessor(loan.getTransactionProcessingStrategyCode());
|
||||||
|
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(),
|
||||||
|
allNonContraTransactionsPostDisbursement, loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||||
|
loan.getActiveCharges());
|
||||||
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
|
mapEntry.getValue().updateLoan(loan);
|
||||||
|
}
|
||||||
|
loan.getLoanTransactions().addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||||
|
}
|
||||||
|
loan.updateLoanSummaryDerivedFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, loan);
|
||||||
|
changes.put(Loan.PARAM_STATUS, LoanEnumerations.status(loan.getLoanStatus()));
|
||||||
|
return changedTransactionDetail;
|
||||||
|
}
|
||||||
|
|
||||||
private void updatePostDatedChecks(Set<PostDatedChecks> postDatedChecks) {
|
private void updatePostDatedChecks(Set<PostDatedChecks> postDatedChecks) {
|
||||||
this.postDatedChecksRepository.saveAll(postDatedChecks);
|
this.postDatedChecksRepository.saveAll(postDatedChecks);
|
||||||
}
|
}
|
||||||
@@ -799,11 +804,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || downPaymentEnabled) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || downPaymentEnabled) {
|
||||||
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
if (configurationDomainService.isPaymentTypeApplicableForDisbursementCharge()) {
|
changedTransactionDetail = disburseLoan(command, configurationDomainService.isPaymentTypeApplicableForDisbursementCharge(),
|
||||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail);
|
paymentDetail, loan, currentUser, changes, scheduleGeneratorDTO);
|
||||||
} else {
|
|
||||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
|
|
||||||
@@ -1132,7 +1134,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loan.updateLoanSummaryDerivedFields();
|
||||||
|
|
||||||
doPostLoanTransactionChecks(loan, newInterestPaymentWaiverTransaction.getTransactionDate(), defaultLoanLifecycleStateMachine);
|
doPostLoanTransactionChecks(loan, newInterestPaymentWaiverTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||||
|
|
||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
@@ -1519,7 +1521,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail,
|
final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail,
|
||||||
defaultLoanLifecycleStateMachine, transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
loanLifecycleStateMachine, transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
||||||
scheduleGeneratorDTO, reversalTxnExternalId);
|
scheduleGeneratorDTO, reversalTxnExternalId);
|
||||||
|
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
@@ -1681,7 +1683,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
newTransaction = this.loanTransactionRepository.saveAndFlush(newTransaction);
|
newTransaction = this.loanTransactionRepository.saveAndFlush(newTransaction);
|
||||||
|
|
||||||
loan.handleChargebackTransaction(newTransaction, defaultLoanLifecycleStateMachine);
|
loan.handleChargebackTransaction(newTransaction, loanLifecycleStateMachine);
|
||||||
|
|
||||||
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
|
|
||||||
@@ -1765,8 +1767,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction,
|
final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction, loanLifecycleStateMachine,
|
||||||
defaultLoanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||||
|
|
||||||
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
@@ -1863,7 +1865,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine, changes,
|
final ChangedTransactionDetail changedTransactionDetail = loan.closeAsWrittenOff(command, loanLifecycleStateMachine, changes,
|
||||||
existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
||||||
|
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
@@ -1937,8 +1939,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||||
ChangedTransactionDetail changedTransactionDetail = loan.close(command, defaultLoanLifecycleStateMachine, changes,
|
ChangedTransactionDetail changedTransactionDetail = loan.close(command, loanLifecycleStateMachine, changes, existingTransactionIds,
|
||||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||||
|
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
@@ -2039,7 +2041,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
changes.put("locale", command.locale());
|
changes.put("locale", command.locale());
|
||||||
changes.put("dateFormat", command.dateFormat());
|
changes.put("dateFormat", command.dateFormat());
|
||||||
|
|
||||||
loan.closeAsMarkedForReschedule(command, defaultLoanLifecycleStateMachine, changes);
|
loan.closeAsMarkedForReschedule(command, loanLifecycleStateMachine, changes);
|
||||||
|
|
||||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||||
|
|
||||||
@@ -2083,7 +2085,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void disburseLoanToLoan(final Loan loan, final JsonCommand command, final BigDecimal amount) {
|
private void disburseLoanToLoan(final Loan loan, final JsonCommand command, final BigDecimal amount) {
|
||||||
|
|
||||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
|
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
|
||||||
|
|
||||||
@@ -2098,7 +2099,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount, final PaymentDetail paymentDetail) {
|
private void disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount, final PaymentDetail paymentDetail) {
|
||||||
|
|
||||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
|
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
|
||||||
|
|
||||||
@@ -2118,7 +2118,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
LoanTransactionType.DISBURSEMENT.getValue(), null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null,
|
LoanTransactionType.DISBURSEMENT.getValue(), null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null,
|
||||||
txnExternalId, loan, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck);
|
txnExternalId, loan, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck);
|
||||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -2136,7 +2135,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
ExternalId externalId = externalIdFactory.create();
|
ExternalId externalId = externalIdFactory.create();
|
||||||
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate, externalId);
|
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate, externalId);
|
||||||
loan.addLoanTransaction(newTransferTransaction);
|
loan.addLoanTransaction(newTransferTransaction);
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine;
|
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan);
|
||||||
|
|
||||||
this.loanTransactionRepository.saveAndFlush(newTransferTransaction);
|
this.loanTransactionRepository.saveAndFlush(newTransferTransaction);
|
||||||
@@ -2159,7 +2158,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate,
|
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate,
|
||||||
externalId);
|
externalId);
|
||||||
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine;
|
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
||||||
if (loan.getTotalOverpaid() != null) {
|
if (loan.getTotalOverpaid() != null) {
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan);
|
||||||
} else {
|
} else {
|
||||||
@@ -2192,7 +2191,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate,
|
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate,
|
||||||
externalId);
|
externalId);
|
||||||
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine;
|
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan);
|
||||||
|
|
||||||
this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction);
|
this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction);
|
||||||
@@ -2209,7 +2208,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
public void rejectLoanTransfer(final Loan loan) {
|
public void rejectLoanTransfer(final Loan loan) {
|
||||||
this.loanAssembler.setHelpers(loan);
|
this.loanAssembler.setHelpers(loan);
|
||||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine;
|
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan);
|
||||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
||||||
@@ -2610,7 +2609,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||||
|
|
||||||
ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(defaultLoanLifecycleStateMachine, existingTransactionIds,
|
ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(loanLifecycleStateMachine, existingTransactionIds,
|
||||||
existingReversedTransactionIds, scheduleGeneratorDTO);
|
existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
@@ -2885,8 +2884,44 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final LocalDate rescheduledRepaymentDate) {
|
final LocalDate rescheduledRepaymentDate) {
|
||||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedEmiAmountParameterName);
|
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedEmiAmountParameterName);
|
||||||
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount,
|
|
||||||
nextPossibleRepaymentDate, rescheduledRepaymentDate);
|
boolean isEmiAmountChanged = false;
|
||||||
|
LoanProduct loanProduct = loan.getLoanProduct();
|
||||||
|
if ((loanProduct.isMultiDisburseLoan() || loanProduct.isCanDefineInstallmentAmount()) && emiAmount != null
|
||||||
|
&& emiAmount.compareTo(loan.retriveLastEmiAmount()) != 0) {
|
||||||
|
if (loanProduct.isMultiDisburseLoan()) {
|
||||||
|
final LocalDate dateValue = null;
|
||||||
|
final boolean isSpecificToInstallment = false;
|
||||||
|
final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = scheduleGeneratorDTO
|
||||||
|
.isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
|
||||||
|
LocalDate effectiveDateFrom = actualDisbursementDate;
|
||||||
|
if (!isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled && actualDisbursementDate.equals(nextPossibleRepaymentDate)) {
|
||||||
|
effectiveDateFrom = nextPossibleRepaymentDate.plusDays(1);
|
||||||
|
}
|
||||||
|
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
||||||
|
effectiveDateFrom, emiAmount, dateValue, isSpecificToInstallment, loan, LoanStatus.ACTIVE.getValue());
|
||||||
|
loan.getLoanTermVariations().add(loanVariationTerms);
|
||||||
|
} else {
|
||||||
|
loan.setFixedEmiAmount(emiAmount);
|
||||||
|
}
|
||||||
|
isEmiAmountChanged = true;
|
||||||
|
}
|
||||||
|
if (rescheduledRepaymentDate != null && loanProduct.isMultiDisburseLoan()) {
|
||||||
|
final boolean isSpecificToInstallment = false;
|
||||||
|
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.DUE_DATE.getValue(),
|
||||||
|
nextPossibleRepaymentDate, emiAmount, rescheduledRepaymentDate, isSpecificToInstallment, loan,
|
||||||
|
LoanStatus.ACTIVE.getValue());
|
||||||
|
loan.getLoanTermVariations().add(loanVariationTerms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loan.isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged
|
||||||
|
|| rescheduledRepaymentDate != null) {
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||||
|
} else {
|
||||||
|
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
|
|
||||||
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
|||||||
+14
-13
@@ -346,8 +346,8 @@ public class LoanAccountConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(LoanWritePlatformService.class)
|
@ConditionalOnMissingBean(LoanWritePlatformService.class)
|
||||||
public LoanWritePlatformService loanWritePlatformService(PlatformSecurityContext context,
|
public LoanWritePlatformService loanWritePlatformService(LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory,
|
||||||
LoanTransactionValidator loanTransactionValidator,
|
PlatformSecurityContext context, LoanTransactionValidator loanTransactionValidator,
|
||||||
LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, LoanRepositoryWrapper loanRepositoryWrapper,
|
LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, LoanRepositoryWrapper loanRepositoryWrapper,
|
||||||
LoanAccountDomainService loanAccountDomainService, NoteRepository noteRepository,
|
LoanAccountDomainService loanAccountDomainService, NoteRepository noteRepository,
|
||||||
LoanTransactionRepository loanTransactionRepository, LoanTransactionRelationRepository loanTransactionRelationRepository,
|
LoanTransactionRepository loanTransactionRepository, LoanTransactionRelationRepository loanTransactionRelationRepository,
|
||||||
@@ -373,17 +373,18 @@ public class LoanAccountConfiguration {
|
|||||||
LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService, ErrorHandler errorHandler,
|
LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService, ErrorHandler errorHandler,
|
||||||
LoanDownPaymentHandlerService loanDownPaymentHandlerService, AccountTransferRepository accountTransferRepository,
|
LoanDownPaymentHandlerService loanDownPaymentHandlerService, AccountTransferRepository accountTransferRepository,
|
||||||
LoanTransactionAssembler loanTransactionAssembler, LoanAccrualsProcessingService loanAccrualsProcessingService) {
|
LoanTransactionAssembler loanTransactionAssembler, LoanAccrualsProcessingService loanAccrualsProcessingService) {
|
||||||
return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer,
|
return new LoanWritePlatformServiceJpaRepositoryImpl(transactionProcessorFactory, context, loanTransactionValidator,
|
||||||
loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository,
|
loanUpdateCommandFromApiJsonDeserializer, loanRepositoryWrapper, loanAccountDomainService, noteRepository,
|
||||||
loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService, calendarInstanceRepository,
|
loanTransactionRepository, loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService,
|
||||||
paymentDetailWritePlatformService, holidayRepository, configurationDomainService, workingDaysRepository,
|
calendarInstanceRepository, paymentDetailWritePlatformService, holidayRepository, configurationDomainService,
|
||||||
accountTransfersWritePlatformService, accountTransfersReadPlatformService, accountAssociationsReadPlatformService,
|
workingDaysRepository, accountTransfersWritePlatformService, accountTransfersReadPlatformService,
|
||||||
loanReadPlatformService, fromApiJsonHelper, calendarRepository, loanScheduleHistoryWritePlatformService,
|
accountAssociationsReadPlatformService, loanReadPlatformService, fromApiJsonHelper, calendarRepository,
|
||||||
loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
|
loanScheduleHistoryWritePlatformService, loanApplicationValidator, accountAssociationRepository,
|
||||||
guarantorDomainService, loanUtilService, loanSummaryWrapper, entityDatatableChecksWritePlatformService,
|
accountTransferDetailRepository, businessEventNotifierService, guarantorDomainService, loanUtilService, loanSummaryWrapper,
|
||||||
transactionProcessingStrategy, codeValueRepository, cashierTransactionDataValidator, glimRepository, loanRepository,
|
entityDatatableChecksWritePlatformService, transactionProcessingStrategy, codeValueRepository,
|
||||||
repaymentWithPostDatedChecksAssembler, postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository,
|
cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
|
||||||
defaultLoanLifecycleStateMachine, loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
|
postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
|
||||||
|
loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
|
||||||
loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, accountTransferRepository,
|
loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, accountTransferRepository,
|
||||||
loanTransactionAssembler, loanAccrualsProcessingService);
|
loanTransactionAssembler, loanAccrualsProcessingService);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -95,8 +95,8 @@ public class CheckLoanRepaymentDueBusinessStepTest {
|
|||||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||||
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(null);
|
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(null);
|
||||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
|
when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
|
||||||
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
||||||
when(repaymentInstallment.getTotalOutstanding(currency)).thenReturn(money);
|
when(repaymentInstallment.getTotalOutstanding(currency)).thenReturn(money);
|
||||||
when(money.isGreaterThanZero()).thenReturn(true);
|
when(money.isGreaterThanZero()).thenReturn(true);
|
||||||
@@ -153,8 +153,8 @@ public class CheckLoanRepaymentDueBusinessStepTest {
|
|||||||
// Loan Product setting overrides global settings
|
// Loan Product setting overrides global settings
|
||||||
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(1);
|
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(1);
|
||||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
|
when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
|
||||||
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
||||||
when(repaymentInstallment.getTotalOutstanding(currency)).thenReturn(money);
|
when(repaymentInstallment.getTotalOutstanding(currency)).thenReturn(money);
|
||||||
when(money.isGreaterThanZero()).thenReturn(true);
|
when(money.isGreaterThanZero()).thenReturn(true);
|
||||||
|
|||||||
+12
-12
@@ -111,8 +111,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
|||||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallment);
|
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallment);
|
||||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
|
when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
|
||||||
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
||||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||||
|
|
||||||
@@ -138,8 +138,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
|||||||
loanInstallmentRepaymentDueDateBefore5Days, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
loanInstallmentRepaymentDueDateBefore5Days, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
||||||
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
|
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
|
||||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
|
when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
|
||||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
||||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||||
// when
|
// when
|
||||||
@@ -166,8 +166,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
|||||||
|
|
||||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallmentPaidOff);
|
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallmentPaidOff);
|
||||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
|
when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
|
||||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
||||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||||
|
|
||||||
@@ -196,8 +196,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
|||||||
when(loanForProcessing.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
when(loanForProcessing.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
||||||
// product configuration overrides global configuration
|
// product configuration overrides global configuration
|
||||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(1);
|
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(1);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
|
when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
|
||||||
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
||||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||||
|
|
||||||
@@ -216,8 +216,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
|||||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||||
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
|
||||||
when(loanForProcessing.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
when(loanForProcessing.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
|
when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
|
||||||
// when
|
// when
|
||||||
Loan processedLoan = underTest.execute(loanForProcessing);
|
Loan processedLoan = underTest.execute(loanForProcessing);
|
||||||
// then - No Business Event raised
|
// then - No Business Event raised
|
||||||
@@ -240,8 +240,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
|||||||
BigDecimal.valueOf(1.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
|
BigDecimal.valueOf(1.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
|
||||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(1);
|
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(1);
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
|
when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
|
||||||
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
when(loanForProcessing.getCurrency()).thenReturn(currency);
|
||||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||||
// when
|
// when
|
||||||
|
|||||||
+2
-2
@@ -117,7 +117,7 @@ public class LoanRepaymentBusinessEventSerializerTest {
|
|||||||
when(loanForProcessing.getId()).thenReturn(1L);
|
when(loanForProcessing.getId()).thenReturn(1L);
|
||||||
when(loanForProcessing.getAccountNumber()).thenReturn("0001");
|
when(loanForProcessing.getAccountNumber()).thenReturn("0001");
|
||||||
when(loanForProcessing.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId"));
|
when(loanForProcessing.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId"));
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0));
|
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0));
|
||||||
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
|
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
|
||||||
when(loanCurrency.getCode()).thenReturn("CODE");
|
when(loanCurrency.getCode()).thenReturn("CODE");
|
||||||
@@ -168,7 +168,7 @@ public class LoanRepaymentBusinessEventSerializerTest {
|
|||||||
when(loanForProcessing.getId()).thenReturn(null);
|
when(loanForProcessing.getId()).thenReturn(null);
|
||||||
when(loanForProcessing.getAccountNumber()).thenReturn("0001");
|
when(loanForProcessing.getAccountNumber()).thenReturn("0001");
|
||||||
when(loanForProcessing.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId"));
|
when(loanForProcessing.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId"));
|
||||||
when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
|
when(loanForProcessing.getSummary()).thenReturn(loanSummary);
|
||||||
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0));
|
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0));
|
||||||
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
|
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
|
||||||
when(loanCurrency.getCode()).thenReturn("CODE");
|
when(loanCurrency.getCode()).thenReturn("CODE");
|
||||||
|
|||||||
+2
-2
@@ -143,7 +143,7 @@ class DefaultLoanLifecycleStateMachineTest {
|
|||||||
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
|
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
|
||||||
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
|
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
|
||||||
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(zero);
|
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(zero);
|
||||||
Mockito.when(loan.getLoanSummary()).thenReturn(loanSummary);
|
Mockito.when(loan.getSummary()).thenReturn(loanSummary);
|
||||||
Mockito.when(loanSummary.getTotalOutstanding(eq(currency))).thenReturn(one);
|
Mockito.when(loanSummary.getTotalOutstanding(eq(currency))).thenReturn(one);
|
||||||
// when
|
// when
|
||||||
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
|
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
|
||||||
@@ -163,7 +163,7 @@ class DefaultLoanLifecycleStateMachineTest {
|
|||||||
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
|
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
|
||||||
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
|
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
|
||||||
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(zero);
|
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(zero);
|
||||||
Mockito.when(loan.getLoanSummary()).thenReturn(loanSummary);
|
Mockito.when(loan.getSummary()).thenReturn(loanSummary);
|
||||||
Mockito.when(loanSummary.getTotalOutstanding(currency)).thenReturn(zero);
|
Mockito.when(loanSummary.getTotalOutstanding(currency)).thenReturn(zero);
|
||||||
// when
|
// when
|
||||||
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
|
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
|
||||||
|
|||||||
+10
-11
@@ -130,22 +130,21 @@ public class LoanRepaymentRescheduleAtDisbursementTest {
|
|||||||
this.loanTransactionHelper.disburseLoanWithRepaymentReschedule(disbursementDate, loanID, adjustRepaymentDate);
|
this.loanTransactionHelper.disburseLoanWithRepaymentReschedule(disbursementDate, loanID, adjustRepaymentDate);
|
||||||
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
|
||||||
ArrayList<HashMap> loanRepaymnetSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, generalResponseSpec,
|
ArrayList<HashMap> loanRepaymentSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, generalResponseSpec,
|
||||||
loanID);
|
loanID);
|
||||||
HashMap firstInstallement = loanRepaymnetSchedule.get(1);
|
HashMap firstInstallment = loanRepaymentSchedule.get(1);
|
||||||
Map<String, Object> expectedvalues = new HashMap<>(3);
|
Map<String, Object> expectedValues = new HashMap<>(3);
|
||||||
Calendar date = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
Calendar date = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||||
date.set(2015, Calendar.MARCH, 16);
|
date.set(2015, Calendar.MARCH, 16);
|
||||||
expectedvalues.put("dueDate", getDateAsArray(date, 0));
|
expectedValues.put("dueDate", getDateAsArray(date, 0));
|
||||||
expectedvalues.put("principalDue", "834.71");
|
expectedValues.put("principalDue", "834.71");
|
||||||
expectedvalues.put("interestDue", "49.32");
|
expectedValues.put("interestDue", "49.32");
|
||||||
expectedvalues.put("feeChargesDue", "0");
|
expectedValues.put("feeChargesDue", "0");
|
||||||
expectedvalues.put("penaltyChargesDue", "0");
|
expectedValues.put("penaltyChargesDue", "0");
|
||||||
expectedvalues.put("totalDueForPeriod", "884.03");
|
expectedValues.put("totalDueForPeriod", "884.03");
|
||||||
|
|
||||||
// VALIDATE REPAYMENT SCHEDULE
|
// VALIDATE REPAYMENT SCHEDULE
|
||||||
verifyLoanRepaymentSchedule(firstInstallement, expectedvalues);
|
verifyLoanRepaymentSchedule(firstInstallment, expectedValues);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCollaterals(List<HashMap> collaterals, Integer collateralId, BigDecimal quantity) {
|
private void addCollaterals(List<HashMap> collaterals, Integer collateralId, BigDecimal quantity) {
|
||||||
|
|||||||
Reference in New Issue
Block a user