Merge pull request #1600 from sachinkulkarni12/CC-36-New

Loan Repayment Rescheduling while disbursement
This commit is contained in:
pramod
2016-01-18 12:08:33 +05:30
8 changed files with 372 additions and 16 deletions
@@ -43,8 +43,9 @@ public class LoanTransactionData {
private final BigDecimal outstandingLoanBalance;
@SuppressWarnings("unused")
private final LocalDate submittedOnDate;
private final boolean manuallyReversed;
@SuppressWarnings("unused")
private final LocalDate possibleNextRepaymentDate;
// templates
final Collection<PaymentTypeData> paymentTypeOptions;
@@ -71,7 +72,7 @@ public class LoanTransactionData {
feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, null, externalId, transfer,
fixedEmiAmount, outstandingLoanBalance,manuallyReversed);
}
public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount,
final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion,
@@ -121,16 +122,74 @@ public class LoanTransactionData {
this.outstandingLoanBalance = outstandingLoanBalance;
this.submittedOnDate = submittedOnDate;
this.manuallyReversed = manuallyReversed;
this.possibleNextRepaymentDate = null;
}
public LoanTransactionData(Long id, LoanTransactionEnumData transactionType, LocalDate date, BigDecimal totalAmount,
BigDecimal principalPortion, BigDecimal interestPortion, BigDecimal feeChargesPortion, BigDecimal penaltyChargesPortion,
BigDecimal overPaymentPortion, BigDecimal unrecognizedIncomePortion, BigDecimal outstandingLoanBalance,final boolean manuallyReversed) {
this(id, null, null, transactionType, null, null, date, totalAmount, principalPortion, interestPortion, feeChargesPortion,
penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, null, null, null, null, outstandingLoanBalance, null,manuallyReversed);
penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, null, null, null, null, outstandingLoanBalance, null,
manuallyReversed);
}
public static LoanTransactionData LoanTransactionDataForDisbursalTemplate(final LoanTransactionEnumData transactionType, final LocalDate expectedDisbursedOnLocalDateForTemplate,
final BigDecimal disburseAmountForTemplate, final Collection<PaymentTypeData> paymentOptions,
final BigDecimal retriveLastEmiAmount, final LocalDate possibleNextRepaymentDate) {
final Long id = null;
final Long officeId = null;
final String officeName = null;
final PaymentDetailData paymentDetailData = null;
final CurrencyData currency = null;
final BigDecimal unrecognizedIncomePortion = null;
final BigDecimal principalPortion = null;;
final BigDecimal interestPortion = null;
final BigDecimal feeChargesPortion = null;
final BigDecimal penaltyChargesPortion = null;
final BigDecimal overpaymentPortion = null;
final String externalId = null;
final BigDecimal outstandingLoanBalance = null;
final AccountTransferData transfer = null;
final LocalDate submittedOnDate = null;
final boolean manuallyReversed = false;
return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currency, expectedDisbursedOnLocalDateForTemplate,
disburseAmountForTemplate, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion,
paymentOptions, transfer, externalId, retriveLastEmiAmount, outstandingLoanBalance, submittedOnDate, manuallyReversed, possibleNextRepaymentDate);
}
public LocalDate dateOf() {
private LoanTransactionData(Long id , final Long officeId, final String officeName, LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData,
final CurrencyData currency, final LocalDate date, BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion,
final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, BigDecimal unrecognizedIncomePortion, Collection<PaymentTypeData> paymentOptions,
final AccountTransferData transfer, final String externalId, final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance,
final LocalDate submittedOnDate, final boolean manuallyReversed, final LocalDate possibleNextRepaymentDate) {
this.id = id;
this.officeId = officeId;
this.officeName = officeName;
this.type = transactionType;
this.paymentDetailData = paymentDetailData;
this.currency = currency;
this.date = date;
this.amount = amount;
this.principalPortion = principalPortion;
this.interestPortion = interestPortion;
this.feeChargesPortion = feeChargesPortion;
this.penaltyChargesPortion = penaltyChargesPortion;
this.unrecognizedIncomePortion = unrecognizedIncomePortion;
this.paymentTypeOptions = paymentOptions;
this.externalId = externalId;
this.transfer = transfer;
this.overpaymentPortion = overpaymentPortion;
this.fixedEmiAmount = fixedEmiAmount;
this.outstandingLoanBalance = outstandingLoanBalance;
this.submittedOnDate = submittedOnDate;
this.manuallyReversed = manuallyReversed;
this.possibleNextRepaymentDate = possibleNextRepaymentDate;
}
public LocalDate dateOf() {
return this.date;
}
@@ -2324,7 +2324,7 @@ public class Loan extends AbstractPersistable<Long> {
}
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser) {
final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser, LocalDate nextPossibleRepaymentDate, Date rescheduledRepaymentDate) {
boolean isEmiAmountChanged = false;
if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.canDefineInstallmentAmount()) && emiAmount != null
&& emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
@@ -2332,13 +2332,19 @@ public class Loan extends AbstractPersistable<Long> {
final Date dateValue = null;
final boolean isSpecificToInstallment = false;
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
actualDisbursementDate.toDate(), emiAmount, dateValue, isSpecificToInstallment, this);
actualDisbursementDate.toDate(), 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.toDate(), emiAmount, rescheduledRepaymentDate, isSpecificToInstallment, this, LoanStatus.ACTIVE.getValue());
this.loanTermVariations.add(loanVariationTerms);
}
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
@@ -5533,6 +5539,12 @@ public class Loan extends AbstractPersistable<Long> {
}
actualDisbursementDate = lastTransactionDate;
updateLoanToLastDisbursalState(actualDisbursementDate);
for (Iterator<LoanTermVariations> iterator = this.loanTermVariations.iterator(); iterator.hasNext();) {
LoanTermVariations loanTermVariations = iterator.next();
if (loanTermVariations.fetchDateValue().isAfter(actualDisbursementDate)) {
iterator.remove();
}
}
reverseExistingTransactionsTillLastDisbursal(actualDisbursementDate);
loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds,
currentUser);
@@ -5621,5 +5633,27 @@ public class Loan extends AbstractPersistable<Long> {
public void setExpectedFirstRepaymentOnDate(Date expectedFirstRepaymentOnDate) {
this.expectedFirstRepaymentOnDate = expectedFirstRepaymentOnDate;
}
/*
* get the next repayment date for rescheduling at the time of disbursement
*/
public LocalDate getNextPossibleRepaymentDateForRescheduling(){
Set<LoanDisbursementDetails> loanDisbursementDetails = this.disbursementDetails;
LocalDate nextRepaymentDate = new LocalDate();
for(LoanDisbursementDetails loanDisbursementDetail : loanDisbursementDetails){
if(loanDisbursementDetail.actualDisbursementDate() == null){
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
if (installment.getDueDate().isEqual(loanDisbursementDetail.expectedDisbursementDateAsLocalDate()) ||
installment.getDueDate().isAfter(loanDisbursementDetail.expectedDisbursementDateAsLocalDate())
&& installment.isNotFullyPaidOff()) {
nextRepaymentDate = installment.getDueDate();
break;
}
}
break;
}
}
return nextRepaymentDate;
}
}
@@ -60,6 +60,17 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
this.isSpecificToInstallment = isSpecificToInstallment;
this.onLoanStatus = loan.status().getValue();
}
public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
final boolean isSpecificToInstallment, final Loan loan, final Integer loanStatus) {
this.loan = loan;
this.termApplicableFrom = termApplicableFrom;
this.termType = termType;
this.decimalValue = decimalValue;
this.dateValue = dateValue;
this.isSpecificToInstallment = isSpecificToInstallment;
this.onLoanStatus = loanStatus;
}
protected LoanTermVariations() {
@@ -64,7 +64,7 @@ public final class LoanEventApiJsonValidator {
"dateFormat", LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
} else {
disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale",
"dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
"dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber", "adjustRepaymentDate",
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
}
@@ -480,11 +480,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
if (paymentDetailsRequired) {
paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
}
final BigDecimal outstandingLoanBalance = null;
final BigDecimal unrecognizedIncomePortion = null;
return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDateForTemplate(),
loan.getDisburseAmountForTemplate(), null, null, null, null, null, unrecognizedIncomePortion, paymentOptions, null, null,
loan.retriveLastEmiAmount(), outstandingLoanBalance, false);
return LoanTransactionData.LoanTransactionDataForDisbursalTemplate(transactionType, loan.getExpectedDisbursedOnLocalDateForTemplate(), loan.getDisburseAmountForTemplate(),
paymentOptions, loan.retriveLastEmiAmount(), loan.getNextPossibleRepaymentDateForRescheduling());
}
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -283,6 +284,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
final LocalDate nextPossibleRepaymentDate = loan.getNextPossibleRepaymentDateForRescheduling();
final Date rescheduledRepaymentDate = command.DateValueOfParameterNamed("adjustRepaymentDate");
// check for product mix validations
checkForProductMixRestrictions(loan);
@@ -333,7 +337,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LocalDate recalculateFrom = null;
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, rescheduledRepaymentDate);
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
loan, null);
@@ -518,6 +522,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions();
final Map<String, Object> changes = new LinkedHashMap<>();
if (disbursalCommand == null) { return changes; }
final LocalDate nextPossibleRepaymentDate = null;
final Date rescheduledRepaymentDate = null;
for (int i = 0; i < disbursalCommand.length; i++) {
final SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i];
@@ -563,7 +570,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
LocalDate recalculateFrom = null;
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, rescheduledRepaymentDate);
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
loan.fetchRepaymentScheduleInstallments(), loan, null);
@@ -2669,11 +2676,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final boolean recalculateSchedule,
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate nextPossibleRepaymentDate, final Date rescheduledRepaymentDate) {
AppUser currentUser = getAppUserIfPresent();
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount, currentUser);
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount, currentUser,
nextPossibleRepaymentDate, rescheduledRepaymentDate);
}
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {