MIFOSX-811: ensure undo disbursement regenerated schedule when expected and actual disbursement dates are different.
This commit is contained in:
+46
-31
@@ -449,7 +449,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
||||||
totalChargeAmt);
|
totalChargeAmt);
|
||||||
|
|
||||||
// NOTE: must add new loan charge to set of loan charges before reporcessing the repayment schedule.
|
// NOTE: must add new loan charge to set of loan charges before
|
||||||
|
// reporcessing the repayment schedule.
|
||||||
setOfLoanCharges().add(loanCharge);
|
setOfLoanCharges().add(loanCharge);
|
||||||
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
@@ -480,11 +481,11 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
* Creates a loanTransaction for "Apply Charge Event" with transaction date
|
* Creates a loanTransaction for "Apply Charge Event" with transaction date
|
||||||
* set to "suppliedTransactionDate". The newly created transaction is also
|
* set to "suppliedTransactionDate". The newly created transaction is also
|
||||||
* added to the Loan on which this method is called.
|
* added to the Loan on which this method is called.
|
||||||
*
|
*
|
||||||
* If "suppliedTransactionDate" is not passed Id, the transaction date is
|
* If "suppliedTransactionDate" is not passed Id, the transaction date is
|
||||||
* set to the loans due date if the due date is lesser than todays date. If
|
* set to the loans due date if the due date is lesser than todays date. If
|
||||||
* not, the transaction date is set to todays date
|
* not, the transaction date is set to todays date
|
||||||
*
|
*
|
||||||
* @param loanCharge
|
* @param loanCharge
|
||||||
* @param suppliedTransactionDate
|
* @param suppliedTransactionDate
|
||||||
* @return
|
* @return
|
||||||
@@ -520,7 +521,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleChargePaidTransaction(final LoanCharge charge, final LoanTransaction chargesPayment,
|
private void handleChargePaidTransaction(final LoanCharge charge, final LoanTransaction chargesPayment,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, Integer installmentNumber) {
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) {
|
||||||
chargesPayment.updateLoan(this);
|
chargesPayment.updateLoan(this);
|
||||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, chargesPayment.getAmount(getCurrency())
|
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, chargesPayment.getAmount(getCurrency())
|
||||||
.getAmount());
|
.getAmount());
|
||||||
@@ -538,7 +539,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
if (installmentNumber == null && charge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())) {
|
if (installmentNumber == null && charge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())) {
|
||||||
chargePaymentInstallments.add(installment);
|
chargePaymentInstallments.add(installment);
|
||||||
break;
|
break;
|
||||||
}else if(installmentNumber != null && installment.getInstallmentNumber().equals(installmentNumber)){
|
} else if (installmentNumber != null && installment.getInstallmentNumber().equals(installmentNumber)) {
|
||||||
chargePaymentInstallments.add(installment);
|
chargePaymentInstallments.add(installment);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -604,7 +605,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
* TODO Vishwas Currently we do not allow removing a loan charge
|
* TODO Vishwas Currently we do not allow removing a loan charge
|
||||||
* after a loan is approved (hence there is no need to adjust any
|
* after a loan is approved (hence there is no need to adjust any
|
||||||
* loan transactions).
|
* loan transactions).
|
||||||
*
|
*
|
||||||
* Consider removing this block of code or logically completing it
|
* Consider removing this block of code or logically completing it
|
||||||
* for the future by getting the list of affected Transactions
|
* for the future by getting the list of affected Transactions
|
||||||
***/
|
***/
|
||||||
@@ -662,7 +663,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
* TODO Vishwas Currently we do not allow waiving updating loan
|
* TODO Vishwas Currently we do not allow waiving updating loan
|
||||||
* charge after a loan is approved (hence there is no need to adjust
|
* charge after a loan is approved (hence there is no need to adjust
|
||||||
* any loan transactions).
|
* any loan transactions).
|
||||||
*
|
*
|
||||||
* Consider removing this block of code or logically completing it
|
* Consider removing this block of code or logically completing it
|
||||||
* for the future by getting the list of affected Transactions
|
* for the future by getting the list of affected Transactions
|
||||||
***/
|
***/
|
||||||
@@ -707,8 +708,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public BigDecimal getTotalInterest() {
|
public BigDecimal getTotalInterest() {
|
||||||
return this.loanSummaryWrapper.calculateTotalInterestCharged(
|
return this.loanSummaryWrapper.calculateTotalInterestCharged(this.repaymentScheduleInstallments, getCurrency()).getAmount();
|
||||||
this.repaymentScheduleInstallments, getCurrency()).getAmount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal calculatePerInstallmentChargeAmount(final LoanCharge loanCharge) {
|
private BigDecimal calculatePerInstallmentChargeAmount(final LoanCharge loanCharge) {
|
||||||
@@ -729,7 +729,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
* @param installment
|
* @param installment
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private Money calculateInstallmentChargeAmount(final ChargeCalculationType calculationType, final BigDecimal percentage,
|
private Money calculateInstallmentChargeAmount(final ChargeCalculationType calculationType, final BigDecimal percentage,
|
||||||
final LoanRepaymentScheduleInstallment installment) {
|
final LoanRepaymentScheduleInstallment installment) {
|
||||||
Money amount = Money.zero(getCurrency());
|
Money amount = Money.zero(getCurrency());
|
||||||
Money percentOf = Money.zero(getCurrency());
|
Money percentOf = Money.zero(getCurrency());
|
||||||
@@ -751,8 +751,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
Integer loanInstallmentNumber) {
|
final Integer loanInstallmentNumber) {
|
||||||
|
|
||||||
validateLoanIsNotClosed(loanCharge);
|
validateLoanIsNotClosed(loanCharge);
|
||||||
|
|
||||||
@@ -790,7 +790,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
* TODO Vishwas Currently we do not allow waiving fully paid loan
|
* TODO Vishwas Currently we do not allow waiving fully paid loan
|
||||||
* charge and waiving partially paid loan charges only waives the
|
* charge and waiving partially paid loan charges only waives the
|
||||||
* remaining amount.
|
* remaining amount.
|
||||||
*
|
*
|
||||||
* Consider removing this block of code or logically completing it
|
* Consider removing this block of code or logically completing it
|
||||||
* for the future by getting the list of affected Transactions
|
* for the future by getting the list of affected Transactions
|
||||||
***/
|
***/
|
||||||
@@ -894,7 +894,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) {
|
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) {
|
||||||
clearInstallmentLoanCharge();
|
clearInstallmentLoanCharge();
|
||||||
this.repaymentScheduleInstallments.clear();
|
this.repaymentScheduleInstallments.clear();
|
||||||
|
|
||||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
||||||
|
|
||||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||||
@@ -909,12 +909,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
updateLoanScheduleDependentDerivedFields();
|
updateLoanScheduleDependentDerivedFields();
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearInstallmentLoanCharge(){
|
private void clearInstallmentLoanCharge() {
|
||||||
for(LoanCharge loanCharge:setOfLoanCharges()){
|
for (final LoanCharge loanCharge : setOfLoanCharges()) {
|
||||||
if(loanCharge.isInstalmentFee()){
|
if (loanCharge.isInstalmentFee()) {
|
||||||
loanCharge.clearLoanInstallmentCharges();
|
loanCharge.clearLoanInstallmentCharges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1669,7 +1669,10 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return chargesPayment;
|
return chargesPayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> undoDisbursal(final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
public Map<String, Object> undoDisbursal(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||||
|
final ApplicationCurrency applicationCurrency, final List<Long> existingTransactionIds,
|
||||||
|
final List<Long> existingReversedTransactionIds, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||||
|
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||||
|
|
||||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
||||||
|
|
||||||
@@ -1680,6 +1683,17 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
this.loanStatus = statusEnum.getValue();
|
this.loanStatus = statusEnum.getValue();
|
||||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||||
|
|
||||||
|
final LocalDate actualDisbursementDate = getDisbursementDate();
|
||||||
|
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
|
||||||
|
// clear off actual disbusrement date so schedule regeneration
|
||||||
|
// uses expected date.
|
||||||
|
this.actualDisbursementDate = null;
|
||||||
|
this.disbursedBy = null;
|
||||||
|
|
||||||
|
regenerateRepaymentSchedule(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate,
|
||||||
|
isHolidayEnabled, holidays, workingDays);
|
||||||
|
}
|
||||||
|
|
||||||
this.actualDisbursementDate = null;
|
this.actualDisbursementDate = null;
|
||||||
this.disbursedBy = null;
|
this.disbursedBy = null;
|
||||||
actualChanges.put("actualDisbursementDate", "");
|
actualChanges.put("actualDisbursementDate", "");
|
||||||
@@ -1757,7 +1771,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||||
final boolean allowTransactionsOnNonWorkingDay, final LoanTransaction paymentTransaction, Integer installmentNumber) {
|
final boolean allowTransactionsOnNonWorkingDay, final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
||||||
|
|
||||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate());
|
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate());
|
||||||
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
||||||
@@ -2995,22 +3009,23 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public Set<LoanCharge> charges() {
|
public Set<LoanCharge> charges() {
|
||||||
return this.charges;
|
return this.charges;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<LoanInstallmentCharge> generateInstallmentLoanCharges(final LoanCharge loanCharge){
|
public Set<LoanInstallmentCharge> generateInstallmentLoanCharges(final LoanCharge loanCharge) {
|
||||||
Set<LoanInstallmentCharge> loanChargePerInstallments = new HashSet<LoanInstallmentCharge>();
|
final Set<LoanInstallmentCharge> loanChargePerInstallments = new HashSet<LoanInstallmentCharge>();
|
||||||
if(loanCharge.isInstalmentFee()){
|
if (loanCharge.isInstalmentFee()) {
|
||||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if(loanCharge.getChargeCalculation().isFlat()){
|
if (loanCharge.getChargeCalculation().isFlat()) {
|
||||||
amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||||
}else{
|
} else {
|
||||||
amount = calculateInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage(), installment).getAmount();
|
amount = calculateInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage(), installment)
|
||||||
|
.getAmount();
|
||||||
}
|
}
|
||||||
LoanInstallmentCharge loanInstallmentCharge = new LoanInstallmentCharge(amount, loanCharge, installment);
|
final LoanInstallmentCharge loanInstallmentCharge = new LoanInstallmentCharge(amount, loanCharge, installment);
|
||||||
loanChargePerInstallments.add(loanInstallmentCharge);
|
loanChargePerInstallments.add(loanInstallmentCharge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loanChargePerInstallments;
|
return loanChargePerInstallments;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+22
-5
@@ -434,9 +434,28 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
checkClientOrGroupActive(loan);
|
checkClientOrGroupActive(loan);
|
||||||
removeLoanCycle(loan);
|
removeLoanCycle(loan);
|
||||||
|
|
||||||
|
//
|
||||||
|
final MonetaryCurrency currency = loan.getCurrency();
|
||||||
|
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||||
|
|
||||||
|
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByLoanId(loan.getId(),
|
||||||
|
CalendarEntityType.LOANS.getValue());
|
||||||
|
final LocalDate actualDisbursementDate = loan.getDisbursementDate();
|
||||||
|
final LocalDate calculatedRepaymentsStartingFromDate = getCalculatedRepaymentsStartingFromDate(actualDisbursementDate, loan,
|
||||||
|
calendarInstance);
|
||||||
|
|
||||||
|
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
|
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||||
|
actualDisbursementDate.toDate());
|
||||||
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
|
|
||||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||||
final Map<String, Object> changes = loan.undoDisbursal(existingTransactionIds, existingReversedTransactionIds);
|
|
||||||
|
final Map<String, Object> changes = loan.undoDisbursal(this.loanScheduleFactory, applicationCurrency, existingTransactionIds,
|
||||||
|
existingReversedTransactionIds, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
|
||||||
|
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
this.loanRepository.saveAndFlush(loan);
|
this.loanRepository.saveAndFlush(loan);
|
||||||
this.accountTransfersWritePlatformService.reverseAllTransactions(loanId, PortfolioAccountType.LOAN);
|
this.accountTransfersWritePlatformService.reverseAllTransactions(loanId, PortfolioAccountType.LOAN);
|
||||||
@@ -449,9 +468,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final MonetaryCurrency currency = loan.getCurrency();
|
|
||||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
|
||||||
|
|
||||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
|
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
|
||||||
existingTransactionIds, existingReversedTransactionIds);
|
existingTransactionIds, existingReversedTransactionIds);
|
||||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||||
@@ -1621,7 +1637,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
public void applyChargeForOverdueLoans() throws JobExecutionException {
|
public void applyChargeForOverdueLoans() throws JobExecutionException {
|
||||||
|
|
||||||
final Long penaltyWaitPeriodValue = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
final Long penaltyWaitPeriodValue = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
final Collection<OverdueLoanScheduleData> overdueLoanScheduledInstallments = this.loanReadPlatformService.retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(penaltyWaitPeriodValue);
|
final Collection<OverdueLoanScheduleData> overdueLoanScheduledInstallments = this.loanReadPlatformService
|
||||||
|
.retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(penaltyWaitPeriodValue);
|
||||||
|
|
||||||
if (!overdueLoanScheduledInstallments.isEmpty()) {
|
if (!overdueLoanScheduledInstallments.isEmpty()) {
|
||||||
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) {
|
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) {
|
||||||
|
|||||||
Reference in New Issue
Block a user