Merge pull request #1049 from pramodn02/develop
MIFOSX-1418 : added regeneration of accrual entries
This commit is contained in:
+1
-1
@@ -810,7 +810,7 @@ public class AccountingScenarioIntegrationTest {
|
||||
.withRepaymentAfterEvery(this.LP_REPAYMENT_PERIOD).withNumberOfRepayments(this.LP_REPAYMENTS).withRepaymentTypeAsMonth()
|
||||
.withinterestRatePerPeriod(this.LP_INTEREST_RATE).withInterestRateFrequencyTypeAsMonths()
|
||||
.withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRulePeriodicAccrual(accounts)
|
||||
.build(null);
|
||||
.withDaysInMonth("30").withDaysInYear("365").build(null);
|
||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -31,7 +31,7 @@ public class LoanProductTestBuilder {
|
||||
public static final String MIFOS_STANDARD_STRATEGY = "1";
|
||||
// private static final String HEAVENS_FAMILY_STRATEGY ="2";
|
||||
// private static final String CREO_CORE_STRATEGY ="3";
|
||||
public static final String RBI_INDIA_STRATEGY ="4";
|
||||
public static final String RBI_INDIA_STRATEGY = "4";
|
||||
private static final String NONE = "1";
|
||||
private static final String CASH_BASED = "2";
|
||||
private static final String ACCRUAL_PERIODIC = "3";
|
||||
@@ -63,7 +63,7 @@ public class LoanProductTestBuilder {
|
||||
private Boolean multiDisburseLoan = false;
|
||||
private String outstandingLoanBalance = "35000";
|
||||
private String maxTrancheCount = "35000";
|
||||
|
||||
|
||||
private Boolean isInterestRecalculationEnabled = false;
|
||||
private String daysInYearType = "1";
|
||||
private String daysInMonthType = "1";
|
||||
@@ -328,4 +328,13 @@ public class LoanProductTestBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withDaysInMonth(final String daysInMonthType) {
|
||||
this.daysInMonthType = daysInMonthType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withDaysInYear(final String daysInYearType) {
|
||||
this.daysInYearType = daysInYearType;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+37
-2
@@ -984,9 +984,36 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
updateLoanScheduleDependentDerivedFields();
|
||||
updateLoanSummaryDerivedFields();
|
||||
resetAccruals();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* method to reverse all the accrual entries
|
||||
*/
|
||||
private void resetAccruals() {
|
||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { return; }
|
||||
List<LoanTransaction> transactions = this.getLoanTransactions();
|
||||
for (final LoanTransaction transaction : transactions) {
|
||||
if (transaction.isAccrual()) {
|
||||
transaction.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAccrualsPresent() {
|
||||
boolean isAccrualsPresent = false;
|
||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { return isAccrualsPresent; }
|
||||
List<LoanTransaction> transactions = this.getLoanTransactions();
|
||||
for (final LoanTransaction transaction : transactions) {
|
||||
if (transaction.isAccrual() && transaction.isNotReversed()) {
|
||||
isAccrualsPresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isAccrualsPresent;
|
||||
}
|
||||
|
||||
private void updateLoanScheduleDependentDerivedFields() {
|
||||
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
|
||||
this.actualMaturityDate = determineExpectedMaturityDate().toDate();
|
||||
@@ -3218,7 +3245,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return matchesCurrentLoanOfficer;
|
||||
}
|
||||
|
||||
private LocalDate getInterestChargedFromDate() {
|
||||
public LocalDate getInterestChargedFromDate() {
|
||||
LocalDate interestChargedFrom = null;
|
||||
if (this.interestChargedFromDate != null) {
|
||||
interestChargedFrom = new LocalDate(this.interestChargedFromDate);
|
||||
@@ -3415,7 +3442,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return this.loanProduct.isPeriodicAccrualAccountingEnabled();
|
||||
}
|
||||
|
||||
private Long productId() {
|
||||
public Long productId() {
|
||||
return this.loanProduct.getId();
|
||||
}
|
||||
|
||||
@@ -4170,4 +4197,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
public List<LoanRepaymentScheduleInstallment> fetchRepaymentScheduleInstallments() {
|
||||
return this.repaymentScheduleInstallments;
|
||||
}
|
||||
|
||||
public LocalDate getAccruedTill() {
|
||||
LocalDate accruedTill = null;
|
||||
if (this.accruedTill != null) {
|
||||
accruedTill = new LocalDate(this.accruedTill);
|
||||
}
|
||||
return accruedTill;
|
||||
}
|
||||
}
|
||||
+9
@@ -30,4 +30,13 @@ public interface LoanAccountDomainService {
|
||||
PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
||||
|
||||
LocalDate getCalculatedRepaymentsStartingFromDate(LocalDate actualDisbursementDate, Loan loan, CalendarInstance calendarInstance);
|
||||
|
||||
/**
|
||||
* This method is to recalculate and accrue the income till the last accrued
|
||||
* date. this method is used when the schedule changes due to interest
|
||||
* recalculation
|
||||
*
|
||||
* @param loan
|
||||
*/
|
||||
void recalculateAccruals(Loan loan);
|
||||
}
|
||||
|
||||
+76
-3
@@ -8,8 +8,10 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
@@ -22,6 +24,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation
|
||||
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.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
@@ -37,9 +40,14 @@ import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
|
||||
import org.mifosplatform.portfolio.common.domain.DaysInMonthType;
|
||||
import org.mifosplatform.portfolio.common.domain.DaysInYearType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanAccrualWritePlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.note.domain.Note;
|
||||
@@ -68,6 +76,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository;
|
||||
private final LoanAccrualWritePlatformService accrualWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanAccountDomainServiceJpa(final LoanAssembler loanAccountAssembler, final LoanRepository loanRepository,
|
||||
@@ -79,7 +88,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final AccountTransferRepository accountTransferRepository, final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository) {
|
||||
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository,
|
||||
final LoanAccrualWritePlatformService accrualWritePlatformService) {
|
||||
this.loanAccountAssembler = loanAccountAssembler;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanTransactionRepository = loanTransactionRepository;
|
||||
@@ -94,6 +104,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository;
|
||||
this.accrualWritePlatformService = accrualWritePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -187,6 +198,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
|
||||
recalculateAccruals(loan);
|
||||
builderResult.withEntityId(newRepaymentTransaction.getId()) //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
.withClientId(loan.getClientId()) //
|
||||
@@ -210,7 +222,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
try {
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
|
||||
@@ -270,6 +282,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
recalculateAccruals(loan);
|
||||
return newPaymentTransaction;
|
||||
}
|
||||
|
||||
@@ -364,7 +377,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
|
||||
return disbursementTransaction;
|
||||
}
|
||||
|
||||
@@ -402,6 +414,67 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
return calculatedRepaymentsStartingFromDate;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService#recalculateAccruals(org.mifosplatform.portfolio.loanaccount.domain.Loan)
|
||||
*/
|
||||
@Override
|
||||
public void recalculateAccruals(Loan loan) {
|
||||
LocalDate accruedTill = loan.getAccruedTill();
|
||||
if (!loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || accruedTill == null || loan.isAccrualsPresent()) { return; }
|
||||
Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>();
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
|
||||
Long loanId = loan.getId();
|
||||
DaysInMonthType daysInMonth = loan.repaymentScheduleDetail().fetchDaysInMonthType();
|
||||
DaysInYearType daysInYear = loan.repaymentScheduleDetail().fetchDaysInYearType();
|
||||
Long officeId = loan.getOfficeId();
|
||||
LocalDate accrualStartDate = null;
|
||||
PeriodFrequencyType repaymentFrequency = loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType();
|
||||
Integer repayEvery = loan.repaymentScheduleDetail().getRepayEvery();
|
||||
LocalDate interestCalculatedFrom = loan.getInterestChargedFromDate();
|
||||
Long loanProductId = loan.productId();
|
||||
MonetaryCurrency currency = loan.getCurrency();
|
||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
CurrencyData currencyData = applicationCurrency.toData();
|
||||
Set<LoanCharge> loanCharges = loan.charges();
|
||||
BigDecimal accruedInterestIncome = null;
|
||||
BigDecimal accruedFeeIncome = null;
|
||||
BigDecimal accruedPenaltyIncome = null;
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (!accruedTill.isBefore(installment.getDueDate())
|
||||
|| (accruedTill.isAfter(installment.getFromDate()) && !accruedTill.isAfter(installment.getDueDate()))) {
|
||||
BigDecimal dueDateFeeIncome = BigDecimal.ZERO;
|
||||
BigDecimal dueDatePenaltyIncome = BigDecimal.ZERO;
|
||||
LocalDate chargesTillDate = installment.getDueDate();
|
||||
if (!accruedTill.isAfter(installment.getDueDate())) {
|
||||
chargesTillDate = accruedTill;
|
||||
}
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), chargesTillDate)) {
|
||||
if (loanCharge.isFeeCharge()) {
|
||||
dueDateFeeIncome = dueDateFeeIncome.add(loanCharge.amount());
|
||||
} else if (loanCharge.isPenaltyCharge()) {
|
||||
dueDatePenaltyIncome = dueDatePenaltyIncome.add(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
}
|
||||
LoanScheduleAccrualData accrualData = new LoanScheduleAccrualData(loanId, officeId, accrualStartDate,
|
||||
daysInMonth.getValue(), daysInYear.getValue(), repaymentFrequency, repayEvery, installment.getDueDate(),
|
||||
installment.getFromDate(), installment.getId(), loanProductId,
|
||||
installment.getInterestCharged(currency).getAmount(), installment.getFeeChargesCharged(currency).getAmount(),
|
||||
installment.getPenaltyChargesCharged(currency).getAmount(), accruedInterestIncome, accruedFeeIncome,
|
||||
accruedPenaltyIncome, currencyData, dueDateFeeIncome, dueDatePenaltyIncome, interestCalculatedFrom);
|
||||
loanScheduleAccrualDatas.add(accrualData);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!loanScheduleAccrualDatas.isEmpty()) {
|
||||
this.accrualWritePlatformService.addPeriodicAccruals(accruedTill, loanScheduleAccrualDatas);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
||||
final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId);
|
||||
if (transferTransaction != null) {
|
||||
|
||||
+36
-1
@@ -146,6 +146,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms.getLoanTermPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery());
|
||||
Money balanceForcalculation = outstandingBalance;
|
||||
// reduce principal is the early payment, will processed as per the
|
||||
// reschedule strategy
|
||||
if (reducePrincipal.isGreaterThanZero()) {
|
||||
switch (loanApplicationTerms.getRescheduleStrategyMethod()) {
|
||||
case REDUCE_EMI_AMOUNT:
|
||||
@@ -211,10 +213,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
|
||||
}
|
||||
|
||||
// this block is to identify interest based on late/early payment
|
||||
if (diffAmt != null && !diffAmt.isEmpty()) {
|
||||
BigDecimal interestDueToLatePayment = BigDecimal.ZERO;
|
||||
BigDecimal interestReducedDueToEarlyPayment = BigDecimal.ZERO;
|
||||
for (RecalculationDetail detail : diffAmt) {
|
||||
// will increase the principal portion and reduces interest
|
||||
// as per the number of days. and also identifies reduce
|
||||
// principal for reschedule strategy
|
||||
if (!detail.isLatePayment() && detail.getStartDate().isAfter(periodStartDate)
|
||||
&& !detail.getStartDate().isAfter(scheduledDueDate)) {
|
||||
reducePrincipal = reducePrincipal.plus(detail.getAmount());
|
||||
@@ -234,7 +240,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
interestReducedDueToEarlyPayment = interestReducedDueToEarlyPayment.add(loanApplicationTerms.interestRateFor(
|
||||
this.paymentPeriodsInOneYearCalculator, mc, detail.getAmount(), detail.getStartDate(), scheduledDueDate));
|
||||
|
||||
} else if (detail.isLatePayment() && detail.isOverlapping(periodStartDate, scheduledDueDate)
|
||||
}
|
||||
// calculates the interest for late payment and increase
|
||||
// the interest for the installment
|
||||
else if (detail.isLatePayment() && detail.isOverlapping(periodStartDate, scheduledDueDate)
|
||||
&& periodStartDate.isBefore(LocalDate.now())) {
|
||||
LocalDate fromDate = periodStartDate;
|
||||
LocalDate toDate = scheduledDueDate;
|
||||
@@ -312,6 +321,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
}
|
||||
|
||||
// this block is to add extra re-payment schedules with interest portion
|
||||
// if the last payment is missed
|
||||
if (diffAmt != null && !diffAmt.isEmpty() && !periodStartDate.isAfter(LocalDate.now())) {
|
||||
Map<LocalDate, RecalculationDetail> processDetails = new TreeMap<>();
|
||||
for (RecalculationDetail detail : diffAmt) {
|
||||
@@ -518,6 +529,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return cumulative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method calls schedule regeneration by passing transactions one after
|
||||
* another(this is done mainly to handle the scenario where interest or fee
|
||||
* of over due installment should be collected before collecting principal )
|
||||
*/
|
||||
@Override
|
||||
public LoanScheduleModel rescheduleNextInstallments(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
@@ -570,6 +586,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return recalculateFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method calls regenerate schedule for a particular set of transactions
|
||||
* till all the installments are processed to identify early or late
|
||||
* payments and will be used in regeneration of schedule
|
||||
*/
|
||||
private LoanScheduleModel recalculateInstallment(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
@@ -593,6 +614,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return loanScheduleModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method identifies late or early payments for schedule recalculation.
|
||||
*/
|
||||
private RecalculatedSchedule recalculateInterest(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
||||
@@ -638,6 +662,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
Map<LocalDate, Money> earlyPaymentMap = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(
|
||||
transactionsForInstallment, currency, processinstallmets, installment, recalculationDates);
|
||||
|
||||
// this block is to create early payment entries for schedule
|
||||
// generation
|
||||
for (Map.Entry<LocalDate, Money> entry : earlyPaymentMap.entrySet()) {
|
||||
LocalDate startDate = entry.getKey();
|
||||
if (unpaidPricipal.isGreaterThan(entry.getValue())) {
|
||||
@@ -655,6 +681,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
Money totalOutstanding = installment.getTotalOutstanding(currency);
|
||||
boolean reduceStartDate = false;
|
||||
|
||||
// this block is to identify late payment based on the rest
|
||||
// calculation date
|
||||
while (totalOutstanding.isGreaterThanZero() && startDate.isBefore(LocalDate.now())) {
|
||||
LocalDate recalculateFrom = getNextRestScheduleDate(startDate.minusDays(1), loanApplicationTerms, isHolidayEnabled,
|
||||
holidays, workingDays);
|
||||
@@ -725,6 +753,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return installments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to identify which transaction did the payment for current
|
||||
* installment
|
||||
*/
|
||||
private void applyRest(List<LoanTransaction> loanTransactions, LocalDate from, LocalDate to,
|
||||
LoanRepaymentScheduleInstallment installment,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, MonetaryCurrency currency,
|
||||
@@ -760,6 +792,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return nextScheduleDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns the amount payable to close the loan account as of today.
|
||||
*/
|
||||
@Override
|
||||
public Money fetchPrepaymentAmount(final List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
||||
final LoanApplicationTerms applicationTerms, final MathContext mc) {
|
||||
|
||||
+5
@@ -5,8 +5,11 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.service;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||
|
||||
|
||||
public interface LoanAccrualWritePlatformService {
|
||||
@@ -17,4 +20,6 @@ public interface LoanAccrualWritePlatformService {
|
||||
|
||||
String addPeriodicAccruals(LocalDate tilldate);
|
||||
|
||||
String addPeriodicAccruals(LocalDate tilldate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas);
|
||||
|
||||
}
|
||||
|
||||
+14
-5
@@ -95,7 +95,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
||||
return addPeriodicAccruals(tilldate, loanScheduleAccrualDatas);
|
||||
}
|
||||
|
||||
private String addPeriodicAccruals(final LocalDate tilldate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas) {
|
||||
@Override
|
||||
public String addPeriodicAccruals(final LocalDate tilldate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Set<Long> loansIds = new HashSet<>();
|
||||
LocalDate accruredTill = null;
|
||||
@@ -129,16 +130,18 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
||||
|
||||
private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanScheduleAccrualData accrualData,
|
||||
final LocalDate accruredTill) throws Exception {
|
||||
LocalDate interestStartDate = accrualData.getFromDateAsLocaldate();
|
||||
LocalDate interestStartDate = accrualData.getFromDateAsLocaldate();
|
||||
boolean isInterestStartDateEqulsInstallmentStartdate = true;
|
||||
if (accrualData.getInterestCalculatedFrom() != null
|
||||
&& accrualData.getFromDateAsLocaldate().isBefore(accrualData.getInterestCalculatedFrom())) {
|
||||
isInterestStartDateEqulsInstallmentStartdate = false;
|
||||
if (accrualData.getInterestCalculatedFrom().isBefore(accrualData.getDueDateAsLocaldate())) {
|
||||
interestStartDate = accrualData.getInterestCalculatedFrom();
|
||||
} else {
|
||||
interestStartDate = accrualData.getDueDateAsLocaldate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int totalNumberOfDays = Days.daysBetween(interestStartDate, accrualData.getDueDateAsLocaldate()).getDays();
|
||||
LocalDate startDate = accruredTill;
|
||||
if (startDate == null) {
|
||||
@@ -156,10 +159,16 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
||||
int daysInSchedule = Days.daysBetween(interestStartDate, tilldate).getDays();
|
||||
switch (accrualData.getRepaymentFrequency()) {
|
||||
case MONTHS:
|
||||
totalNumberOfDays = calculateTotalNumberOfDaysForMonth(totalNumberOfDays, accrualData);
|
||||
int numberOfDaysInMonth = calculateTotalNumberOfDaysForMonth(totalNumberOfDays, accrualData);
|
||||
if (!(!isInterestStartDateEqulsInstallmentStartdate && totalNumberOfDays < numberOfDaysInMonth)) {
|
||||
totalNumberOfDays = numberOfDaysInMonth;
|
||||
}
|
||||
break;
|
||||
case YEARS:
|
||||
totalNumberOfDays = calculateTotalNumberOfDaysForYear(totalNumberOfDays, accrualData);
|
||||
int numberOfDaysinYear = calculateTotalNumberOfDaysForYear(totalNumberOfDays, accrualData);
|
||||
if (!(!isInterestStartDateEqulsInstallmentStartdate && totalNumberOfDays < numberOfDaysinYear)) {
|
||||
totalNumberOfDays = numberOfDaysinYear;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
+4
-2
@@ -1424,6 +1424,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
.append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,")
|
||||
.append("loan.accrued_till as accruedTill, loan.repayment_period_frequency_enum as frequencyEnum, ")
|
||||
.append("loan.interest_calculated_from_date as interestCalculatedFrom, ")
|
||||
.append("loan.days_in_month_enum as daysInMonth, ")
|
||||
.append("loan.days_in_year_enum as daysInYear, ")
|
||||
.append("loan.repay_every as repayEvery,")
|
||||
.append("(select sum(lc.amount) as dueamount from m_loan_charge lc where lc.loan_id = loan.id and lc.is_active = 1 and lc.waived = 0 and lc.is_penalty = 0 and lc.due_for_collection_as_of_date > ls.fromdate and lc.due_for_collection_as_of_date<= if(:tilldate < ls.duedate, :tilldate , ls.duedate)) as feedueIn,")
|
||||
.append("(select sum(lc.amount) as dueamount from m_loan_charge lc where lc.loan_id = loan.id and lc.is_active = 1 and lc.waived = 0 and lc.is_penalty = 1 and lc.due_for_collection_as_of_date > ls.fromdate and lc.due_for_collection_as_of_date<= if(:tilldate < ls.duedate, :tilldate , ls.duedate)) as penalitydueIn,")
|
||||
@@ -1448,8 +1450,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final Long officeId = rs.getLong("officeId");
|
||||
final LocalDate accruedTill = JdbcSupport.getLocalDate(rs, "accruedTill");
|
||||
final LocalDate interestCalculatedFrom = JdbcSupport.getLocalDate(rs, "interestCalculatedFrom");
|
||||
final Integer numberOfDaysInMonth = 30;
|
||||
final Integer numberOfDaysInYear = 360;
|
||||
final Integer numberOfDaysInMonth = JdbcSupport.getInteger(rs, "daysInMonth");;
|
||||
final Integer numberOfDaysInYear = JdbcSupport.getInteger(rs, "daysInYear");;
|
||||
final Integer frequencyEnum = JdbcSupport.getInteger(rs, "frequencyEnum");
|
||||
final Integer repayEvery = JdbcSupport.getInteger(rs, "repayEvery");
|
||||
final PeriodFrequencyType frequency = PeriodFrequencyType.fromInt(frequencyEnum);
|
||||
|
||||
+14
-7
@@ -315,6 +315,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
}
|
||||
|
||||
final Set<LoanCharge> loanCharges = loan.charges();
|
||||
@@ -340,7 +341,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
updateRecurringCalendarDatesForInterestRecalculation(loan);
|
||||
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(loan.getId()) //
|
||||
@@ -526,7 +527,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||
}
|
||||
updateRecurringCalendarDatesForInterestRecalculation(loan);
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
@@ -766,6 +769,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(transactionId) //
|
||||
@@ -857,7 +862,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(waiveInterestTransaction.getId()) //
|
||||
@@ -925,7 +930,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(writeoff.getId()) //
|
||||
@@ -998,7 +1003,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (possibleClosingTransaction != null) {
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
CommandProcessingResult result = null;
|
||||
if (possibleClosingTransaction != null) {
|
||||
|
||||
@@ -2043,6 +2048,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
.withClientId(loan.getClientId()) //
|
||||
@@ -2091,7 +2097,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
.withClientId(loan.getClientId()) //
|
||||
@@ -2205,8 +2211,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||
workingDays = this.workingDaysRepository.findOne();
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance,
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(loanScheduleFactory,
|
||||
applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance,
|
||||
existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -2218,6 +2224,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
}
|
||||
|
||||
private void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
||||
|
||||
Reference in New Issue
Block a user