Merge branch 'foreclosureissues' into develop
This commit is contained in:
+75
-45
@@ -2986,13 +2986,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
boolean reprocess = true;
|
||||
|
||||
if (isTransactionChronologicallyLatest && adjustedTransaction == null
|
||||
&& loanTransaction.getTransactionDate().isEqual(LocalDate.now()) && currentInstallment != null
|
||||
&& loanTransaction.getTransactionDate().isEqual(DateUtils.getLocalDateOfTenant()) && currentInstallment != null
|
||||
&& currentInstallment.getTotalOutstanding(getCurrency()).isEqualTo(loanTransaction.getAmount(getCurrency()))) {
|
||||
reprocess = false;
|
||||
}
|
||||
|
||||
if (isTransactionChronologicallyLatest && adjustedTransaction == null
|
||||
&& (!reprocess || !this.repaymentScheduleDetail().isInterestRecalculationEnabled())) {
|
||||
&& (!reprocess || !this.repaymentScheduleDetail().isInterestRecalculationEnabled()) && !isForeclosure()) {
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments,
|
||||
charges());
|
||||
reprocess = false;
|
||||
@@ -6003,9 +6003,10 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return loanProduct;
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment fetchLoanForeclosureDetail(final LocalDate closureDate) {
|
||||
Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding());
|
||||
public LoanRepaymentScheduleInstallment fetchLoanForeclosureDetail(final LocalDate closureDate) {
|
||||
Money[] receivables = retriveIncomeOutstandingTillDate(closureDate);
|
||||
Money totalPrincipal = (Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding()));
|
||||
totalPrincipal = totalPrincipal.minus(receivables[3]);
|
||||
final List<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
|
||||
final LocalDate currentDate = DateUtils.getLocalDateOfTenant();
|
||||
return new LoanRepaymentScheduleInstallment(null, 0, currentDate, currentDate, totalPrincipal.getAmount(),
|
||||
@@ -6013,45 +6014,65 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public Money[] retriveIncomeOutstandingTillDate(final LocalDate paymentDate) {
|
||||
Money[] balances = new Money[3];
|
||||
final MonetaryCurrency currency = getCurrency();
|
||||
Money[] balances = new Money[4];
|
||||
final MonetaryCurrency currency = getCurrency();
|
||||
Money interest = Money.zero(currency);
|
||||
Money paidFromFutureInstallments = Money.zero(currency);
|
||||
Money fee = Money.zero(currency);
|
||||
Money penalty = Money.zero(currency);
|
||||
Money penalty = Money.zero(currency);
|
||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||
if (installment.isNotFullyPaidOff()) {
|
||||
if (!installment.getDueDate().isAfter(paymentDate)) {
|
||||
interest = interest.plus(installment.getInterestOutstanding(currency));
|
||||
fee = fee.plus(installment.getFeeChargesOutstanding(currency));
|
||||
penalty = penalty.plus(installment.getPenaltyChargesOutstanding(currency));
|
||||
} else if (installment.getFromDate().isBefore(paymentDate)) {
|
||||
Money[] balancesForCurrentPeroid = fetchInterestFeeAndPenalty(paymentDate, currency, installment);
|
||||
interest = interest.plus(balancesForCurrentPeroid[0]);
|
||||
fee = fee.plus(balancesForCurrentPeroid[1]);
|
||||
penalty = penalty.plus(balancesForCurrentPeroid[2]);
|
||||
if (!installment.getDueDate().isAfter(paymentDate)) {
|
||||
interest = interest.plus(installment.getInterestOutstanding(currency));
|
||||
penalty = penalty.plus(installment.getPenaltyChargesOutstanding(currency));
|
||||
fee = fee.plus(installment.getFeeChargesOutstanding(currency));
|
||||
} else if (installment.getFromDate().isBefore(paymentDate)) {
|
||||
Money[] balancesForCurrentPeroid = fetchInterestFeeAndPenaltyTillDate(paymentDate, currency, installment);
|
||||
if (balancesForCurrentPeroid[0].isGreaterThan(installment.getInterestPaid(currency))) {
|
||||
interest = interest.plus(balancesForCurrentPeroid[0].minus(installment.getInterestPaid(currency)));
|
||||
} else {
|
||||
paidFromFutureInstallments = paidFromFutureInstallments.plus(installment.getInterestPaid(currency).minus(
|
||||
balancesForCurrentPeroid[0]));
|
||||
}
|
||||
if (balancesForCurrentPeroid[1].isGreaterThan(installment.getFeeChargesPaid(currency))) {
|
||||
fee = fee.plus(balancesForCurrentPeroid[1].minus(installment.getFeeChargesPaid(currency)));
|
||||
} else {
|
||||
paidFromFutureInstallments = paidFromFutureInstallments.plus(installment.getFeeChargesPaid(currency).minus(
|
||||
balancesForCurrentPeroid[1]));
|
||||
}
|
||||
if (balancesForCurrentPeroid[2].isGreaterThan(installment.getPenaltyChargesPaid(currency))) {
|
||||
penalty = penalty.plus(balancesForCurrentPeroid[2].minus(installment.getPenaltyChargesPaid(currency)));
|
||||
} else {
|
||||
paidFromFutureInstallments = paidFromFutureInstallments.plus(installment.getPenaltyChargesPaid(currency).minus(
|
||||
balancesForCurrentPeroid[2]));
|
||||
}
|
||||
} else if (installment.getDueDate().isAfter(paymentDate)) {
|
||||
paidFromFutureInstallments = paidFromFutureInstallments.plus(installment.getInterestPaid(currency))
|
||||
.plus(installment.getPenaltyChargesPaid(currency)).plus(installment.getFeeChargesPaid(currency));
|
||||
}
|
||||
|
||||
}
|
||||
balances[0] = interest;
|
||||
balances[1] = fee;
|
||||
balances[2] = penalty;
|
||||
balances[3] = paidFromFutureInstallments;
|
||||
return balances;
|
||||
}
|
||||
|
||||
private Money[] fetchInterestFeeAndPenalty(final LocalDate paymentDate, final MonetaryCurrency currency, final LoanRepaymentScheduleInstallment installment) {
|
||||
private Money[] fetchInterestFeeAndPenaltyTillDate(final LocalDate paymentDate, final MonetaryCurrency currency, final LoanRepaymentScheduleInstallment installment) {
|
||||
Money penaltyForCurrentPeriod = Money.zero(getCurrency());
|
||||
Money feeForCurrentPeriod = Money.zero(getCurrency());
|
||||
Money interestForCurrentPeriod=Money.zero(getCurrency());
|
||||
int totalPeriodDays = Days.daysBetween(installment.getFromDate(), installment.getDueDate()).getDays();
|
||||
int tillDays = Days.daysBetween(installment.getFromDate(), paymentDate).getDays();
|
||||
Money interestForCurrentPeriod = Money.of(getCurrency(),BigDecimal.valueOf(calculateInterestForDays(totalPeriodDays, installment.getInterestOutstanding(getCurrency())
|
||||
int tillDays = Days.daysBetween(installment.getFromDate(), paymentDate).getDays();
|
||||
interestForCurrentPeriod = Money.of(getCurrency(),BigDecimal.valueOf(calculateInterestForDays(totalPeriodDays, installment.getInterestCharged(getCurrency())
|
||||
.getAmount(), tillDays)));
|
||||
for (LoanCharge loanCharge : this.charges) {
|
||||
if (loanCharge.isActive()
|
||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), paymentDate)) {
|
||||
if (loanCharge.isPenaltyCharge()) {
|
||||
penaltyForCurrentPeriod = loanCharge.getAmountOutstanding(getCurrency());
|
||||
penaltyForCurrentPeriod = loanCharge.getAmount(getCurrency());
|
||||
} else {
|
||||
feeForCurrentPeriod = loanCharge.getAmountOutstanding(currency);
|
||||
feeForCurrentPeriod = loanCharge.getAmount(currency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6071,15 +6092,15 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
balances[0] = balances[1] = balances[2] = Money.zero(currency);
|
||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||
if (installment.getDueDate().isEqual(paymentDate)){
|
||||
Money interest = installment.getInterestOutstanding(currency);
|
||||
Money fee = installment.getFeeChargesOutstanding(currency);
|
||||
Money penalty = installment.getPenaltyChargesOutstanding(currency);
|
||||
Money interest = installment.getInterestCharged(currency);
|
||||
Money fee = installment.getFeeChargesCharged(currency);
|
||||
Money penalty = installment.getPenaltyChargesCharged(currency);
|
||||
balances[0] = interest;
|
||||
balances[1] = fee;
|
||||
balances[2] = penalty;
|
||||
break;
|
||||
}else if(installment.getDueDate().isAfter(paymentDate) && installment.getFromDate().isBefore(paymentDate)){
|
||||
balances = fetchInterestFeeAndPenalty(paymentDate, currency, installment);
|
||||
balances = fetchInterestFeeAndPenaltyTillDate(paymentDate, currency, installment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -6125,25 +6146,26 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
receivables[2] = receivablePenalty;
|
||||
return receivables;
|
||||
}
|
||||
|
||||
public void reverseAccrualsAfter(final LocalDate tillDate) {
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
if (transaction.isAccrual() && transaction.getTransactionDate().isAfter(tillDate)) {
|
||||
transaction.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleForeClosureTransactions(final List<LoanTransaction> repaymentTransaction,
|
||||
final LocalDate foreClosureDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public ChangedTransactionDetail handleForeClosureTransactions(final LoanTransaction repaymentTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ScheduleGeneratorDTO scheduleGeneratorDTO,
|
||||
final AppUser appUser) {
|
||||
|
||||
LoanEvent event = LoanEvent.LOAN_FORECLOSURE;
|
||||
|
||||
validateAccountStatus(event);
|
||||
|
||||
MonetaryCurrency currency = getCurrency();
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
|
||||
loanRepaymentScheduleTransactionProcessor.processTransactionsFromDerivedFields(repaymentTransaction, currency,
|
||||
this.repaymentScheduleInstallments, charges());
|
||||
this.loanTransactions.addAll(repaymentTransaction);
|
||||
validateForForeclosure(repaymentTransaction.getTransactionDate());
|
||||
this.loanSubStatus = LoanSubStatus.FORECLOSED.getValue();
|
||||
updateLoanSummaryDerivedFields();
|
||||
doPostLoanTransactionChecks(foreClosureDate, loanLifecycleStateMachine);
|
||||
applyAccurals(appUser);
|
||||
return handleRepaymentOrRecoveryOrWaiverTransaction(repaymentTransaction, loanLifecycleStateMachine, null, scheduleGeneratorDTO,
|
||||
appUser);
|
||||
}
|
||||
|
||||
public Money retrieveAccruedAmountAfterDate(final LocalDate tillDate) {
|
||||
@@ -6200,11 +6222,10 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
Money [] balances = retriveIncomeForOverlappingPeriod(transactionDate);
|
||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||
if (!installment.getDueDate().isBefore(transactionDate)) {
|
||||
if (!installment.isPartlyPaid() && !installment.isObligationsMet()) {
|
||||
totalPrincipal = totalPrincipal.plus(installment.getPrincipalOutstanding(currency));
|
||||
totalPrincipal = totalPrincipal.plus(installment.getPrincipal(currency));
|
||||
newInstallments.remove(installment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LocalDate installmentStartDate = getDisbursementDate();
|
||||
@@ -6218,7 +6239,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
installmentStartDate, transactionDate, totalPrincipal.getAmount(),
|
||||
balances[0].getAmount(), balances[1].getAmount(), balances[2].getAmount(), true, null);
|
||||
newInstallment.updateInstallmentNumber(newInstallments.size() + 1);
|
||||
newInstallments.add(newInstallment);
|
||||
newInstallments.add(newInstallment);
|
||||
updateLoanScheduleOnForeclosure(newInstallments);
|
||||
|
||||
Set<LoanCharge> charges = this.charges();
|
||||
@@ -6243,5 +6264,14 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
public Integer getLoanSubStatus() {
|
||||
return this.loanSubStatus;
|
||||
}
|
||||
|
||||
private boolean isForeclosure(){
|
||||
boolean isForeClosure = false;
|
||||
if(this.loanSubStatus != null){
|
||||
isForeClosure = LoanSubStatus.fromInt(loanSubStatus).isForeclosed();
|
||||
}
|
||||
|
||||
return isForeClosure;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+31
-19
@@ -586,26 +586,27 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = null;
|
||||
AppUser appUser = getAppUserIfPresent();
|
||||
final LoanRepaymentScheduleInstallment foreCloseDetail = loan.fetchLoanForeclosureDetail(foreClosureDate);
|
||||
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()
|
||||
&& (loan.getAccruedTill() == null || !foreClosureDate.isEqual(loan.getAccruedTill()))) {
|
||||
loan.reverseAccrualsAfter(foreClosureDate);
|
||||
Money[] accruedReceivables = loan.getReceivableIncome(foreClosureDate);
|
||||
Money interestPortion = foreCloseDetail.getInterestCharged(currency).minus(accruedReceivables[0]);
|
||||
Money feePortion = foreCloseDetail.getFeeChargesCharged(currency).minus(accruedReceivables[1]);
|
||||
Money penaltyPortion = foreCloseDetail.getPenaltyChargesCharged(currency).minus(accruedReceivables[2]);
|
||||
Money total = interestPortion.plus(feePortion).plus(penaltyPortion);
|
||||
if (total.isGreaterThanZero()) {
|
||||
LoanTransaction accrualTransaction = LoanTransaction.accrual(loan, loan.getOffice(), total, interestPortion, feePortion,
|
||||
penaltyPortion, foreClosureDate);
|
||||
LoanTransaction accrualTransaction = LoanTransaction.accrueTransaction(loan, loan.getOffice(), foreClosureDate,
|
||||
total.getAmount(), interestPortion.getAmount(), feePortion.getAmount(), penaltyPortion.getAmount(), appUser);
|
||||
LocalDate fromDate = loan.getDisbursementDate();
|
||||
if (loan.getAccruedTill() != null) {
|
||||
fromDate = loan.getAccruedTill();
|
||||
}
|
||||
LoanRepaymentScheduleInstallment installment = fetchLoanRepaymentScheduleInstallment(fromDate, loan);
|
||||
installment.updateAccrualPortion(interestPortion, feePortion, penaltyPortion);
|
||||
accrualTransaction.updateCreatedDate(createdDate.toDate());
|
||||
createdDate = createdDate.plusSeconds(1);
|
||||
newTransactions.add(accrualTransaction);
|
||||
loan.getLoanTransactions().add(accrualTransaction);
|
||||
Set<LoanChargePaidBy> accrualCharges = accrualTransaction.getLoanChargesPaid();
|
||||
for (LoanCharge loanCharge : loan.charges()) {
|
||||
if (loanCharge.isActive()
|
||||
@@ -619,36 +620,32 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Money interestPayable = foreCloseDetail.getInterestCharged(currency);
|
||||
|
||||
Money feePayable = foreCloseDetail.getFeeChargesCharged(currency);
|
||||
Money penaltyPayable = foreCloseDetail.getPenaltyChargesCharged(currency);
|
||||
|
||||
Money payPrincipal = foreCloseDetail.getPrincipal(currency);
|
||||
Money payPrincipal = foreCloseDetail.getPrincipal(currency);
|
||||
loan.updateInstallmentsPostDate(foreClosureDate);
|
||||
|
||||
LoanTransaction payment = null;
|
||||
|
||||
|
||||
if (payPrincipal.plus(interestPayable).plus(feePayable).plus(penaltyPayable).isGreaterThanZero()) {
|
||||
AppUser appUser = null;
|
||||
final PaymentDetail paymentDetail = null;
|
||||
String externalId = null;
|
||||
String externalId = null;
|
||||
final LocalDateTime currentDateTime = DateUtils.getLocalDateTimeOfTenant();
|
||||
payment = LoanTransaction.repayment(loan.getOffice(), payPrincipal.plus(interestPayable).plus(feePayable).plus(penaltyPayable),
|
||||
paymentDetail, foreClosureDate, externalId, currentDateTime, appUser);
|
||||
createdDate = createdDate.plusSeconds(1);
|
||||
payment.updateCreatedDate(createdDate.toDate());
|
||||
payment.updateComponents(payPrincipal, interestPayable, feePayable, penaltyPayable);
|
||||
payment.updateLoan(loan);
|
||||
newTransactions.add(payment);
|
||||
}
|
||||
|
||||
List<Long> transactionIds = new ArrayList<>();
|
||||
loan.handleForeClosureTransactions(newTransactions, foreClosureDate, defaultLoanLifecycleStateMachine());
|
||||
for (LoanTransaction newTransaction : newTransactions) {
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newTransaction);
|
||||
transactionIds.add(newTransaction.getId());
|
||||
}
|
||||
changes.put("transactions", transactionIds);
|
||||
changes.put("eventAmount", payPrincipal.getAmount().negate());
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.handleForeClosureTransactions(payment,
|
||||
defaultLoanLifecycleStateMachine(), scheduleGeneratorDTO, appUser);
|
||||
|
||||
/***
|
||||
* TODO Vishwas Batch save is giving me a
|
||||
@@ -658,6 +655,22 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
* recorded against the loan)
|
||||
***/
|
||||
|
||||
for (LoanTransaction newTransaction : newTransactions) {
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newTransaction);
|
||||
transactionIds.add(newTransaction.getId());
|
||||
}
|
||||
changes.put("transactions", transactionIds);
|
||||
changes.put("eventAmount", payPrincipal.getAmount().negate());
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
|
||||
// update loan with references to the newly created transactions
|
||||
loan.getLoanTransactions().add(mapEntry.getValue());
|
||||
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
@@ -667,7 +680,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
|
||||
|
||||
return changes;
|
||||
|
||||
}
|
||||
|
||||
+5
@@ -791,4 +791,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
.plus(getInterestWrittenOff(currency));
|
||||
return getInterestAccrued(currency).minus(interestAccountedFor);
|
||||
}
|
||||
|
||||
public Money getTotalPaid(final MonetaryCurrency currency) {
|
||||
return getPenaltyChargesPaid(currency).plus(getFeeChargesPaid(currency)).plus(getInterestPaid(currency))
|
||||
.plus(getPrincipalCompleted(currency));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -98,7 +98,7 @@ public interface LoanWritePlatformService {
|
||||
|
||||
CommandProcessingResult makeLoanRefund(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult addAndDeleteLoanDisburseDetails(Long loanId, JsonCommand command);
|
||||
CommandProcessingResult addAndDeleteLoanDisburseDetails(Long loanId, JsonCommand command);
|
||||
|
||||
void applyOverdueChargesForLoan(Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas);
|
||||
|
||||
|
||||
+1
-2
@@ -2898,7 +2898,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.transactionDateParamName, element);
|
||||
this.loanEventApiJsonValidator.validateLoanForeclosure(command.json());
|
||||
loan.validateForForeclosure(transactionDate);
|
||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||
changes.put("transactionDate", transactionDate);
|
||||
|
||||
@@ -2906,7 +2905,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
LoanRescheduleRequest loanRescheduleRequest = null;
|
||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.getRepaymentScheduleInstallments(),
|
||||
loan, loanRescheduleRequest);
|
||||
loan.updateInstallmentsPostDate(transactionDate);
|
||||
|
||||
|
||||
final Map<String, Object> modifications = this.loanAccountDomainService.foreCloseLoan(loan, transactionDate, noteText);
|
||||
changes.putAll(modifications);
|
||||
|
||||
Reference in New Issue
Block a user