MIFOSX-811: ensure undo disbursement regenerated schedule when expected and actual disbursement dates are different.

This commit is contained in:
Keith Woodlock
2013-12-03 15:07:56 +00:00
parent 2c74e0cdfe
commit 046b9aaf2d
2 changed files with 68 additions and 36 deletions
@@ -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
@@ -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;
} }
@@ -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) {
@@ -752,7 +752,7 @@ 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);
@@ -912,9 +912,9 @@ public class Loan extends AbstractPersistable<Long> {
} }
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);
@@ -2996,17 +3010,18 @@ public class Loan extends AbstractPersistable<Long> {
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);
} }
} }
@@ -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) {