From 93a39abf75947e671bc92140e1d1f2d10c6e8ed5 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Tue, 3 Dec 2013 15:07:56 +0000 Subject: [PATCH] MIFOSX-811: ensure undo disbursement regenerated schedule when expected and actual disbursement dates are different. --- .../portfolio/loanaccount/domain/Loan.java | 77 +++++++++++-------- ...WritePlatformServiceJpaRepositoryImpl.java | 27 +++++-- 2 files changed, 68 insertions(+), 36 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index acbec2897..bca35d424 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -449,7 +449,8 @@ public class Loan extends AbstractPersistable { loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(), 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); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory @@ -480,11 +481,11 @@ public class Loan extends AbstractPersistable { * Creates a loanTransaction for "Apply Charge Event" with transaction date * set to "suppliedTransactionDate". The newly created transaction is also * added to the Loan on which this method is called. - * + * * 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 * not, the transaction date is set to todays date - * + * * @param loanCharge * @param suppliedTransactionDate * @return @@ -520,7 +521,7 @@ public class Loan extends AbstractPersistable { } private void handleChargePaidTransaction(final LoanCharge charge, final LoanTransaction chargesPayment, - final LoanLifecycleStateMachine loanLifecycleStateMachine, Integer installmentNumber) { + final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) { chargesPayment.updateLoan(this); final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, chargesPayment.getAmount(getCurrency()) .getAmount()); @@ -538,7 +539,7 @@ public class Loan extends AbstractPersistable { if (installmentNumber == null && charge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())) { chargePaymentInstallments.add(installment); break; - }else if(installmentNumber != null && installment.getInstallmentNumber().equals(installmentNumber)){ + } else if (installmentNumber != null && installment.getInstallmentNumber().equals(installmentNumber)) { chargePaymentInstallments.add(installment); break; } @@ -604,7 +605,7 @@ public class Loan extends AbstractPersistable { * TODO Vishwas Currently we do not allow removing a loan charge * after a loan is approved (hence there is no need to adjust any * loan transactions). - * + * * Consider removing this block of code or logically completing it * for the future by getting the list of affected Transactions ***/ @@ -662,7 +663,7 @@ public class Loan extends AbstractPersistable { * TODO Vishwas Currently we do not allow waiving updating loan * charge after a loan is approved (hence there is no need to adjust * any loan transactions). - * + * * Consider removing this block of code or logically completing it * for the future by getting the list of affected Transactions ***/ @@ -707,8 +708,7 @@ public class Loan extends AbstractPersistable { * @return */ public BigDecimal getTotalInterest() { - return this.loanSummaryWrapper.calculateTotalInterestCharged( - this.repaymentScheduleInstallments, getCurrency()).getAmount(); + return this.loanSummaryWrapper.calculateTotalInterestCharged(this.repaymentScheduleInstallments, getCurrency()).getAmount(); } private BigDecimal calculatePerInstallmentChargeAmount(final LoanCharge loanCharge) { @@ -729,7 +729,7 @@ public class Loan extends AbstractPersistable { * @param installment * @return */ - private Money calculateInstallmentChargeAmount(final ChargeCalculationType calculationType, final BigDecimal percentage, + private Money calculateInstallmentChargeAmount(final ChargeCalculationType calculationType, final BigDecimal percentage, final LoanRepaymentScheduleInstallment installment) { Money amount = Money.zero(getCurrency()); Money percentOf = Money.zero(getCurrency()); @@ -751,8 +751,8 @@ public class Loan extends AbstractPersistable { } public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine, - final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds, - Integer loanInstallmentNumber) { + final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds, + final Integer loanInstallmentNumber) { validateLoanIsNotClosed(loanCharge); @@ -790,7 +790,7 @@ public class Loan extends AbstractPersistable { * TODO Vishwas Currently we do not allow waiving fully paid loan * charge and waiving partially paid loan charges only waives the * remaining amount. - * + * * Consider removing this block of code or logically completing it * for the future by getting the list of affected Transactions ***/ @@ -894,7 +894,7 @@ public class Loan extends AbstractPersistable { public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) { clearInstallmentLoanCharge(); this.repaymentScheduleInstallments.clear(); - + for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) { if (scheduledLoanInstallment.isRepaymentPeriod()) { @@ -909,12 +909,12 @@ public class Loan extends AbstractPersistable { updateLoanScheduleDependentDerivedFields(); updateLoanSummaryDerivedFields(); - + } - - private void clearInstallmentLoanCharge(){ - for(LoanCharge loanCharge:setOfLoanCharges()){ - if(loanCharge.isInstalmentFee()){ + + private void clearInstallmentLoanCharge() { + for (final LoanCharge loanCharge : setOfLoanCharges()) { + if (loanCharge.isInstalmentFee()) { loanCharge.clearLoanInstallmentCharges(); } } @@ -1669,7 +1669,10 @@ public class Loan extends AbstractPersistable { return chargesPayment; } - public Map undoDisbursal(final List existingTransactionIds, final List existingReversedTransactionIds) { + public Map undoDisbursal(final LoanScheduleGeneratorFactory loanScheduleFactory, + final ApplicationCurrency applicationCurrency, final List existingTransactionIds, + final List existingReversedTransactionIds, final LocalDate calculatedRepaymentsStartingFromDate, + final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays) { final Map actualChanges = new LinkedHashMap(); @@ -1680,6 +1683,17 @@ public class Loan extends AbstractPersistable { this.loanStatus = statusEnum.getValue(); 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.disbursedBy = null; actualChanges.put("actualDisbursementDate", ""); @@ -1757,7 +1771,7 @@ public class Loan extends AbstractPersistable { public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine, final List existingTransactionIds, final List existingReversedTransactionIds, final boolean allowTransactionsOnHoliday, final List 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()); validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays); @@ -3010,22 +3024,23 @@ public class Loan extends AbstractPersistable { public Set charges() { return this.charges; } - - public Set generateInstallmentLoanCharges(final LoanCharge loanCharge){ - Set loanChargePerInstallments = new HashSet(); - if(loanCharge.isInstalmentFee()){ + + public Set generateInstallmentLoanCharges(final LoanCharge loanCharge) { + final Set loanChargePerInstallments = new HashSet(); + if (loanCharge.isInstalmentFee()) { for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { BigDecimal amount = BigDecimal.ZERO; - if(loanCharge.getChargeCalculation().isFlat()){ - amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments())); - }else{ - amount = calculateInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage(), installment).getAmount(); + if (loanCharge.getChargeCalculation().isFlat()) { + amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments())); + } else { + 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); } } return loanChargePerInstallments; } - + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 54cfd973c..2875c1c47 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -434,9 +434,28 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(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 holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), + actualDisbursementDate.toDate()); + final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); - final Map changes = loan.undoDisbursal(existingTransactionIds, existingReversedTransactionIds); + + final Map changes = loan.undoDisbursal(this.loanScheduleFactory, applicationCurrency, existingTransactionIds, + existingReversedTransactionIds, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays); + if (!changes.isEmpty()) { this.loanRepository.saveAndFlush(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 accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(), existingTransactionIds, existingReversedTransactionIds); this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); @@ -1624,7 +1640,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public void applyChargeForOverdueLoans() throws JobExecutionException { final Long penaltyWaitPeriodValue = this.configurationDomainService.retrievePenaltyWaitPeriod(); - final Collection overdueLoanScheduledInstallments = this.loanReadPlatformService.retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(penaltyWaitPeriodValue); + final Collection overdueLoanScheduledInstallments = this.loanReadPlatformService + .retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(penaltyWaitPeriodValue); if (!overdueLoanScheduledInstallments.isEmpty()) { for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) {