Merge branch 'emmanuelnnaa-MIFOSX-1638' into develop

This commit is contained in:
Vishwas Babu A J
2014-11-20 06:00:55 +05:30
4 changed files with 183 additions and 45 deletions
@@ -4864,4 +4864,17 @@ public class Loan extends AbstractPersistable<Long> {
public Boolean shouldCreateStandingInstructionAtDisbursement() {
return (this.createStandingInstructionAtDisbursement != null) && this.createStandingInstructionAtDisbursement;
}
}
public Collection<LoanCharge> getLoanCharges(LocalDate dueDate) {
Collection<LoanCharge> loanCharges = new ArrayList<>();
for (LoanCharge loanCharge : charges) {
if ((loanCharge.getDueLocalDate() != null) && loanCharge.getDueLocalDate().equals(dueDate)) {
loanCharges.add(loanCharge);
}
}
return loanCharges;
}
}
@@ -794,6 +794,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
Money principalDue = Money.of(currency, period.principalDue());
Money interestDue = Money.of(currency, period.interestDue());
if(principalDue.isZero() && interestDue.isZero()) {
period.updateFeeChargesDue(Money.zero(currency));
period.updatePenaltyChargesDue(Money.zero(currency));
}
Money feeChargesDue = Money.of(currency, period.feeChargesDue());
Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
@@ -150,21 +150,6 @@ public class LoanRescheduleRequestDataValidator {
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)
.failWithCode("repayment.schedule.installment.partly.paid", "Repayment schedule installment is partly paid");
}
if (installment != null) {
if (installment.getPenaltyChargesOutstanding(loan.getCurrency()).isGreaterThanZero()) {
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(
"repayment.schedule.installment.has.an.outstanding.penalty.charge",
"Repayment schedule installment has an outsatnding penalty charge.");
}
if (installment.getFeeChargesOutstanding(loan.getCurrency()).isGreaterThanZero()) {
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(
"repayment.schedule.installment.has.an.outstanding.fee.charge",
"Repayment schedule installment has an outsatnding fee charge.");
}
}
}
if (loanId != null) {
@@ -243,21 +228,6 @@ public class LoanRescheduleRequestDataValidator {
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(
"loan.repayment.schedule.installment." + "obligation.met", "Repayment schedule installment obligation met");
}
if (installment != null) {
if (installment.getPenaltyChargesOutstanding(loan.getCurrency()).isGreaterThanZero()) {
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(
"repayment.schedule.installment.has.an.outstanding.penalty.charge",
"Repayment schedule installment has an outsatnding penalty charge.");
}
if (installment.getFeeChargesOutstanding(loan.getCurrency()).isGreaterThanZero()) {
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(
"repayment.schedule.installment.has.an.outstanding.fee.charge",
"Repayment schedule installment has an outsatnding fee charge.");
}
}
}
if (loanId != null) {
@@ -8,6 +8,8 @@ package org.mifosplatform.portfolio.loanaccount.rescheduleloan.service;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
@@ -17,6 +19,7 @@ import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
@@ -26,23 +29,38 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.holiday.domain.Holiday;
import org.mifosplatform.organisation.holiday.domain.HolidayRepository;
import org.mifosplatform.organisation.holiday.domain.HolidayStatusType;
import org.mifosplatform.organisation.holiday.domain.HolidayRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargePaidByData;
import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator;
@@ -52,6 +70,8 @@ import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanResched
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
import org.mifosplatform.useradministration.domain.AppUser;
@@ -74,10 +94,18 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
private final ConfigurationDomainService configurationDomainService;
private final HolidayRepository holidayRepository;
private final HolidayRepositoryWrapper holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
private final CalendarInstanceRepository calendarInstanceRepository;
private final LoanAccountDomainService loanAccountDomainService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final LoanScheduleGeneratorFactory loanScheduleFactory;
private final LoanTransactionRepository loanTransactionRepository;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final LoanRepository loanRepository;
private final LoanAssembler loanAssembler;
/**
* LoanRescheduleRequestWritePlatformServiceImpl constructor
@@ -90,9 +118,17 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
LoanRescheduleRequestRepository loanRescheduleRequestRepository,
ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, ConfigurationDomainService configurationDomainService,
HolidayRepository holidayRepository, WorkingDaysRepositoryWrapper workingDaysRepository,
HolidayRepositoryWrapper holidayRepository, WorkingDaysRepositoryWrapper workingDaysRepository,
LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService) {
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
final CalendarInstanceRepository calendarInstanceRepository,
final LoanAccountDomainService loanAccountDomainService,
final LoanChargeReadPlatformService loanChargeReadPlatformService,
final LoanScheduleGeneratorFactory loanScheduleFactory,
final LoanTransactionRepository loanTransactionRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService,
final LoanRepository loanRepository,
final LoanAssembler loanAssembler) {
this.loanRepositoryWrapper = loanRepositoryWrapper;
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
this.platformSecurityContext = platformSecurityContext;
@@ -104,6 +140,14 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
this.workingDaysRepository = workingDaysRepository;
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
this.calendarInstanceRepository = calendarInstanceRepository;
this.loanAccountDomainService = loanAccountDomainService;
this.loanChargeReadPlatformService = loanChargeReadPlatformService;
this.loanScheduleFactory = loanScheduleFactory;
this.loanTransactionRepository = loanTransactionRepository;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.loanRepository = loanRepository;
this.loanAssembler = loanAssembler;
}
/**
@@ -267,7 +311,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
.getDisbursementDate().toDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
@@ -286,7 +330,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
Collection<LoanCharge> waiveLoanCharges = new ArrayList<>();
for (LoanRescheduleModelRepaymentPeriod period : periods) {
if (period.isNew()) {
@@ -302,6 +347,9 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
if (repaymentScheduleInstallment.getInstallmentNumber().equals(period.oldPeriodNumber())) {
LocalDate periodDueDate = repaymentScheduleInstallment.getDueDate();
Money zeroAmount = Money.of(currency, new BigDecimal(0));
repaymentScheduleInstallment.updateInstallmentNumber(period.periodNumber());
repaymentScheduleInstallment.updateFromDate(period.periodFromDate());
repaymentScheduleInstallment.updateDueDate(period.periodDueDate());
@@ -309,12 +357,13 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
repaymentScheduleInstallment.updateInterestCharged(period.interestDue());
if (Money.of(currency, period.principalDue()).isZero() && Money.of(currency, period.interestDue()).isZero()
&& repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isZero()
&& repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isZero()
&& repaymentScheduleInstallment.isNotFullyPaidOff()) {
repaymentScheduleInstallment.updateObligationMet(true);
repaymentScheduleInstallment.updateObligationMetOnDate(new LocalDate());
if(repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isGreaterThan(zeroAmount) ||
repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isGreaterThan(zeroAmount)) {
waiveLoanCharges.addAll(loan.getLoanCharges(periodDueDate));
}
}
break;
@@ -329,6 +378,9 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
loan.updateRescheduledByUser(appUser);
loan.updateRescheduledOnDate(new LocalDate());
// waive all loan charges of zero instalments
waiveLoanCharges(loan, waiveLoanCharges);
// update the Loan summary
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true);
@@ -342,6 +394,14 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
// update the status of the request
loanRescheduleRequest.approve(appUser, approvedOnDate);
// update the derived fields of each loan repayments schedule instalments
for (final LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
repaymentScheduleInstallment.updateDerivedFields(currency, new LocalDate());
}
// update the loan object
this.loanRepository.save(loan);
}
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
@@ -357,6 +417,97 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
}
}
/**
* waive all charges in the collection
*
* @param loan Loan object
* @param loanCharges collection of LoanCharge objects
* @return void
**/
private void waiveLoanCharges(Loan loan, Collection<LoanCharge> loanCharges) {
this.loanAssembler.setHelpers(loan);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isNotFullyPaid() && !loanCharge.isWaived()) {
Integer loanInstallmentNumber = null;
if (loanCharge.isInstalmentFee()) {
LoanInstallmentCharge chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
if (chargePerInstallment != null) {
loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
}
}
final Map<String, Object> changes = new LinkedHashMap<>(3);
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
CalendarInstance restCalendarInstance = null;
ApplicationCurrency applicationCurrency = null;
LocalDate calculatedRepaymentsStartingFromDate = null;
List<Holiday> holidays = null;
boolean isHolidayEnabled = false;
WorkingDays workingDays = null;
LocalDate recalculateFrom = null;
LocalDate lastTransactionDate = null;
Long overdurPenaltyWaitPeriod = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
final MonetaryCurrency currency = loan.getCurrency();
applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate(
loan.getDisbursementDate(), loan, calendarInstance);
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
workingDays = this.workingDaysRepository.findOne();
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
}
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
lastTransactionDate);
Money accruedCharge = Money.zero(loan.getCurrency());
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy(
loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber);
for (LoanChargePaidByData chargePaidByData : chargePaidByDatas) {
accruedCharge = accruedCharge.plus(chargePaidByData.getAmount());
}
}
final LoanTransaction loanTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge);
this.loanTransactionRepository.save(loanTransaction);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
}
}
}
private void postJournalEntries(Loan loan, List<Long> existingTransactionIds, List<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
boolean isAccountTransfer = false;
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
final List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
@Override
@Transactional
public CommandProcessingResult reject(JsonCommand jsonCommand) {
@@ -411,7 +562,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
* data integrity violation exception
* @return void
**/
@SuppressWarnings("unused")
private void handleDataIntegrityViolation(final JsonCommand jsonCommand, final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve);
@@ -419,5 +569,4 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
throw new PlatformDataIntegrityException("error.msg.loan.reschedule.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
}