Merge pull request #1055 from pramodn02/develop

MIFOSX-1419 : moved capturing of rest frequency to product
This commit is contained in:
Vishwas Babu A J
2014-09-03 06:58:27 -07:00
27 changed files with 497 additions and 197 deletions
@@ -0,0 +1,9 @@
ALTER TABLE `m_product_loan_recalculation_details`
ADD COLUMN `rest_frequency_type_enum` SMALLINT(1) NOT NULL AFTER `reschedule_strategy_enum`,
ADD COLUMN `rest_frequency_interval` SMALLINT(3) NOT NULL DEFAULT '0' AFTER `rest_frequency_type_enum`,
ADD COLUMN `rest_freqency_date` DATE NULL DEFAULT NULL AFTER `rest_frequency_interval`;
ALTER TABLE `m_loan_recalculation_details`
ADD COLUMN `rest_frequency_type_enum` SMALLINT(1) NOT NULL AFTER `reschedule_strategy_enum`,
ADD COLUMN `rest_frequency_interval` SMALLINT(3) NOT NULL DEFAULT '0' AFTER `rest_frequency_type_enum`,
ADD COLUMN `rest_freqency_date` DATE NULL DEFAULT NULL AFTER `rest_frequency_interval`;
@@ -42,7 +42,6 @@ public class LoanApplicationTestBuilder {
private String loanType = "individual";
private String fixedEmiAmount = "10000";
private String maxOutstandingLoanBalance = "36000";
private String recalculationFrequencyType = "1";
private String graceOnPrincipalPayment = null;
private List<HashMap> disbursementData = null;
private List<HashMap> charges = new ArrayList<HashMap>();
@@ -72,7 +71,6 @@ public class LoanApplicationTestBuilder {
map.put("expectedDisbursementDate", this.expectedDisbursmentDate);
map.put("submittedOnDate", this.submittedOnDate);
map.put("loanType", this.loanType);
map.put("recalculationFrequencyType", this.recalculationFrequencyType);
if (charges != null) {
map.put("charges", charges);
}
@@ -821,5 +821,23 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder validateDateBeforeOrEqual(final LocalDate date) {
if (this.value == null && this.ignoreNullValue) { return this; }
if (this.value != null && date != null) {
final LocalDate dateVal = (LocalDate) this.value;
if (!date.isAfter(dateVal)) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".is.greater.than.date");
final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter)
.append(" must be less than or equal to provided date").append(date);
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), this.parameter, dateVal, date);
this.dataValidationErrors.add(error);
}
}
return this;
}
}
@@ -33,7 +33,6 @@ public interface LoanApiConstants {
public static final String isInterestRecalculationEnabledParameterName = "isInterestRecalculationEnabled";
public static final String daysInYearTypeParameterName = "daysInYearType";
public static final String daysInMonthTypeParameterName = "daysInMonthType";
public static final String recalculationFrequencyTypeParameterName = "recalculationFrequencyType";
public static final String interestRecalculationCompoundingMethodParameterName = "interestRecalculationCompoundingMethod";
public static final String rescheduleStrategyMethodParameterName = "rescheduleStrategyMethod";
@@ -410,7 +410,6 @@ public class LoansApiResource {
Collection<CodeValueData> loanCollateralOptions = null;
Collection<CalendarData> calendarOptions = null;
Collection<PortfolioAccountData> accountLinkingOptions = null;
List<EnumOptionData> interestRecalculationFrequencyTypeOptions = null;
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if (template) {
@@ -452,9 +451,6 @@ public class LoansApiResource {
calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.groupId());
}
interestRecalculationFrequencyTypeOptions = this.dropdownReadPlatformService
.retrieveInterestRecalculationFrequencyTypeOptions();
}
Collection<ChargeData> overdueCharges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanBasicDetails.loanProductId(),
@@ -465,7 +461,7 @@ public class LoansApiResource {
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
overdueCharges, interestRecalculationFrequencyTypeOptions);
overdueCharges);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
@@ -130,7 +130,6 @@ public class LoanAccountData {
private final Collection<ChargeData> chargeOptions;
private final Collection<CodeValueData> loanCollateralOptions;
private final Collection<CalendarData> calendarOptions;
private final List<EnumOptionData> interestRecalculationFrequencyTypeOptions;
@Transient
private final BigDecimal feeChargesAtDisbursementCharged;
@@ -261,7 +260,6 @@ public class LoanAccountData {
final EnumOptionData daysInYearType = null;
final boolean isInterestRecalculationEnabled = false;
final LoanInterestRecalculationData interestRecalculationData = null;
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -277,7 +275,7 @@ public class LoanAccountData {
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, interestRecalculationData, interestRecalculationFrequencyTypeOptions);
isInterestRecalculationEnabled, interestRecalculationData);
}
/**
@@ -371,7 +369,6 @@ public class LoanAccountData {
final EnumOptionData daysInYearType = null;
final boolean isInterestRecalculationEnabled = false;
final LoanInterestRecalculationData interestRecalculationData = null;
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -386,8 +383,7 @@ public class LoanAccountData {
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount,
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData,
interestRecalculationFrequencyTypeOptions);
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData);
}
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
@@ -409,8 +405,7 @@ public class LoanAccountData {
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
acc.interestRecalculationFrequencyTypeOptions);
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
}
/**
@@ -505,7 +500,6 @@ public class LoanAccountData {
final EnumOptionData daysInYearType = null;
final boolean isInterestRecalculationEnabled = false;
final LoanInterestRecalculationData interestRecalculationData = null;
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -520,7 +514,7 @@ public class LoanAccountData {
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance,
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, interestRecalculationFrequencyTypeOptions);
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData);
}
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
@@ -541,8 +535,7 @@ public class LoanAccountData {
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
acc.interestRecalculationFrequencyTypeOptions);
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
}
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
@@ -552,7 +545,7 @@ public class LoanAccountData {
final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions,
final Collection<FundData> fundOptions, final Collection<ChargeData> chargeOptions,
final Collection<CodeValueData> loanPurposeOptions, final Collection<CodeValueData> loanCollateralOptions,
final Integer loanCycleNumber, final List<EnumOptionData> interestRecalculationFrequencyTypeOptions) {
final Integer loanCycleNumber) {
final Long id = null;
final String accountNo = null;
@@ -667,8 +660,7 @@ public class LoanAccountData {
notes, accountLinkingOptions, linkedAccount, disbursementData, product.getMultiDisburseLoan(), fixedEmi,
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(),
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
interestRecalculationFrequencyTypeOptions);
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData());
}
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
@@ -721,8 +713,7 @@ public class LoanAccountData {
acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount,
product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears,
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, product.getDaysInMonthType(),
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
acc.interestRecalculationFrequencyTypeOptions);
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData());
}
/*
@@ -777,7 +768,6 @@ public class LoanAccountData {
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
final LoanProductData product = null;
final Collection<ChargeData> overdueCharges = null;
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -792,7 +782,7 @@ public class LoanAccountData {
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance,
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, interestRecalculationFrequencyTypeOptions);
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData);
}
/*
@@ -812,8 +802,7 @@ public class LoanAccountData {
final Collection<CodeValueData> loanCollateralOptions, final Collection<CalendarData> calendarOptions,
final Collection<NoteData> notes, final Collection<PortfolioAccountData> accountLinkingOptions,
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges,
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions) {
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges) {
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
@@ -831,7 +820,7 @@ public class LoanAccountData {
accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount,
acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, interestRecalculationFrequencyTypeOptions);
acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
}
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
@@ -842,8 +831,7 @@ public class LoanAccountData {
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges,
acc.interestRecalculationFrequencyTypeOptions);
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges);
}
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
@@ -863,8 +851,7 @@ public class LoanAccountData {
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
acc.interestRecalculationFrequencyTypeOptions);
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
}
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
@@ -921,8 +908,7 @@ public class LoanAccountData {
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
acc.interestRecalculationFrequencyTypeOptions);
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
}
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
@@ -945,8 +931,7 @@ public class LoanAccountData {
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData,
acc.interestRecalculationFrequencyTypeOptions);
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData);
}
private LoanAccountData(
@@ -1002,8 +987,7 @@ public class LoanAccountData {
final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing,
final Collection<ChargeData> overdueCharges, final Boolean isNPA, final EnumOptionData daysInMonthType,
final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
final LoanInterestRecalculationData interestRecalculationData,
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions) {
final LoanInterestRecalculationData interestRecalculationData) {
this.id = id;
this.accountNo = accountNo;
this.status = status;
@@ -1147,13 +1131,6 @@ public class LoanAccountData {
this.daysInYearType = daysInYearType;
this.isInterestRecalculationEnabled = isInterestRecalculationEnabled;
this.interestRecalculationData = interestRecalculationData;
if (CollectionUtils.isEmpty(interestRecalculationFrequencyTypeOptions)) {
this.interestRecalculationFrequencyTypeOptions = null;
} else {
this.interestRecalculationFrequencyTypeOptions = interestRecalculationFrequencyTypeOptions;
}
}
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.loanaccount.data;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.calendar.data.CalendarData;
@@ -16,20 +17,30 @@ public class LoanInterestRecalculationData {
private final EnumOptionData rescheduleStrategyType;
@SuppressWarnings("unused")
private final CalendarData calendarData;
private final EnumOptionData recalculationRestFrequencyType;
private final Integer recalculationRestFrequencyInterval;
private final LocalDate recalculationRestFrequencyDate;
public LoanInterestRecalculationData(final Long id, final Long loanId, final EnumOptionData interestRecalculationCompoundingType,
final EnumOptionData rescheduleStrategyType, final CalendarData calendarData) {
final EnumOptionData rescheduleStrategyType, final CalendarData calendarData,
final EnumOptionData recalculationRestFrequencyType, final Integer recalculationRestFrequencyInterval,
final LocalDate recalculationRestFrequencyDate) {
this.id = id;
this.loanId = loanId;
this.interestRecalculationCompoundingType = interestRecalculationCompoundingType;
this.rescheduleStrategyType = rescheduleStrategyType;
this.calendarData = calendarData;
this.recalculationRestFrequencyType = recalculationRestFrequencyType;
this.recalculationRestFrequencyInterval = recalculationRestFrequencyInterval;
this.recalculationRestFrequencyDate = recalculationRestFrequencyDate;
}
public static LoanInterestRecalculationData withCalendarData(final LoanInterestRecalculationData recalculationData,
final CalendarData calendarData) {
return new LoanInterestRecalculationData(recalculationData.id, recalculationData.loanId,
recalculationData.interestRecalculationCompoundingType, recalculationData.rescheduleStrategyType, calendarData);
recalculationData.interestRecalculationCompoundingType, recalculationData.rescheduleStrategyType, calendarData,
recalculationData.recalculationRestFrequencyType, recalculationData.recalculationRestFrequencyInterval,
recalculationData.recalculationRestFrequencyDate);
}
public Long getId() {
@@ -90,10 +90,12 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleG
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -1057,6 +1059,9 @@ public class Loan extends AbstractPersistable<Long> {
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
final LocalDate recalculationRestFrequencyDate = command
.localDateValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyDateParamName);
updateLoanInterestRecalculationSettings(recalculationRestFrequencyDate, command, actualChanges);
final String accountNoParamName = "accountNo";
if (command.isChangeInStringParameterNamed(accountNoParamName, this.accountNumber)) {
@@ -1299,15 +1304,24 @@ public class Loan extends AbstractPersistable<Long> {
/**
* Update interest recalculation settings if product configuration changes
*/
public void updateLoanInterestRecalculationSettings() {
public void updateLoanInterestRecalculationSettings(final LocalDate recalculationRestFrequencyDate, final JsonCommand command,
final Map<String, Object> actualChanges) {
if (isInterestRecalculationEnabledForProduct()) {
Date restFrequencyDate = null;
if (recalculationRestFrequencyDate != null) {
restFrequencyDate = recalculationRestFrequencyDate.toDate();
}
if (this.loanInterestRecalculationDetails == null) {
this.loanInterestRecalculationDetails = this.loanProduct.copyInterestRecalculationSettings();
this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom(this.loanProduct
.getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod(), this.loanProduct
.getProductInterestRecalculationDetails().getRescheduleStrategyMethod(), this.loanProduct
.getProductInterestRecalculationDetails().getRestFrequencyType().getValue(), this.loanProduct
.getProductInterestRecalculationDetails().getRestInterval(), restFrequencyDate);
this.loanInterestRecalculationDetails.updateLoan(this);
} else {
this.loanInterestRecalculationDetails = this.loanProduct
.updateLoanInterestRecalculationDetails(this.loanInterestRecalculationDetails);
this.loanInterestRecalculationDetails.update(command, actualChanges);
}
} else {
this.loanInterestRecalculationDetails = null;
@@ -1479,7 +1493,7 @@ public class Loan extends AbstractPersistable<Long> {
public void loanApplicationSubmittal(final AppUser currentUser, final LoanScheduleModel loanSchedule,
final LoanApplicationTerms loanApplicationTerms, final LoanLifecycleStateMachine lifecycleStateMachine,
final LocalDate submittedOn, final String externalId, final boolean allowTransactionsOnHoliday, final List<Holiday> holidays,
final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) {
final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay, final LocalDate recalculationRestFrequencyDate) {
updateLoanSchedule(loanSchedule);
@@ -1542,7 +1556,15 @@ public class Loan extends AbstractPersistable<Long> {
* enabled
*/
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) {
this.loanInterestRecalculationDetails = this.loanProduct.copyInterestRecalculationSettings();
Date restFrequencyDate = null;
if (recalculationRestFrequencyDate != null) {
restFrequencyDate = recalculationRestFrequencyDate.toDate();
}
this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom(this.loanProduct
.getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod(), this.loanProduct
.getProductInterestRecalculationDetails().getRescheduleStrategyMethod(), this.loanProduct
.getProductInterestRecalculationDetails().getRestFrequencyType().getValue(), this.loanProduct
.getProductInterestRecalculationDetails().getRestInterval(), restFrequencyDate);
this.loanInterestRecalculationDetails.updateLoan(this);
}
@@ -4106,12 +4128,14 @@ public class Loan extends AbstractPersistable<Long> {
loanVariationTermsData.add(data);
}
RecalculationFrequencyType recalculationFrequencyType = this.loanInterestRecalculationDetails().getRestFrequencyType();
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
loanVariationTermsData, getInterestChargedFromDate(), this.loanInterestRecalculationDetails,
calendarInstanceForInterestRecalculation);
calendarInstanceForInterestRecalculation, recalculationFrequencyType);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
@@ -5,14 +5,23 @@
*/
package org.mifosplatform.portfolio.loanaccount.domain;
import java.util.Date;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.springframework.data.jpa.domain.AbstractPersistable;
/**
@@ -43,27 +52,53 @@ public class LoanInterestRecalculationDetails extends AbstractPersistable<Long>
@Column(name = "reschedule_strategy_enum", nullable = false)
private Integer rescheduleStrategyMethod;
@Column(name = "rest_frequency_type_enum", nullable = false)
private Integer restFrequencyType;
@Column(name = "rest_frequency_interval", nullable = false)
private Integer restInterval;
@Temporal(TemporalType.DATE)
@Column(name = "rest_freqency_date")
private Date restFrequencyDate;
protected LoanInterestRecalculationDetails() {
// Default constructor for jpa repository
}
private LoanInterestRecalculationDetails(final Integer interestRecalculationCompoundingMethod, final Integer rescheduleStrategyMethod) {
private LoanInterestRecalculationDetails(final Integer interestRecalculationCompoundingMethod, final Integer rescheduleStrategyMethod,
final Integer restFrequencyType, final Integer restInterval, final Date restFrequencyDate) {
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
this.rescheduleStrategyMethod = rescheduleStrategyMethod;
this.restFrequencyDate = restFrequencyDate;
this.restFrequencyType = restFrequencyType;
this.restInterval = restInterval;
}
public static LoanInterestRecalculationDetails createFrom(final Integer interestRecalculationCompoundingMethod,
final Integer rescheduleStrategyMethod) {
return new LoanInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod);
final Integer rescheduleStrategyMethod, final Integer restFrequencyType, final Integer restInterval,
final Date restFrequencyDate) {
return new LoanInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod, restFrequencyType,
restInterval, restFrequencyDate);
}
public void updateLoan(final Loan loan) {
this.loan = loan;
}
public void update(final Integer interestRecalculationCompoundingMethod, final Integer rescheduleStrategyMethod) {
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
this.rescheduleStrategyMethod = rescheduleStrategyMethod;
public void update(final JsonCommand command, final Map<String, Object> actualChanges) {
if (command.isChangeInLocalDateParameterNamed(LoanProductConstants.recalculationRestFrequencyDateParamName,
getRestFrequencyLocalDate())) {
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
final String valueAsInput = command.stringValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyDateParamName);
actualChanges.put(LoanProductConstants.recalculationRestFrequencyDateParamName, valueAsInput);
actualChanges.put("dateFormat", dateFormatAsInput);
actualChanges.put("locale", localeAsInput);
final LocalDate newValue = command.localDateValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyDateParamName);
this.restFrequencyDate = newValue.toDate();
}
}
public InterestRecalculationCompoundingMethod getInterestRecalculationCompoundingMethod() {
@@ -74,4 +109,20 @@ public class LoanInterestRecalculationDetails extends AbstractPersistable<Long>
return LoanRescheduleStrategyMethod.fromInt(this.rescheduleStrategyMethod);
}
public LocalDate getRestFrequencyLocalDate() {
LocalDate recurrenceOnLocalDate = null;
if (this.restFrequencyDate != null) {
recurrenceOnLocalDate = new LocalDate(this.restFrequencyDate);
}
return recurrenceOnLocalDate;
}
public RecalculationFrequencyType getRestFrequencyType() {
return RecalculationFrequencyType.fromInt(this.restFrequencyType);
}
public Integer getRestInterval() {
return this.restInterval;
}
}
@@ -1022,16 +1022,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
recalculationDates, preCloseTransaction);
}
@SuppressWarnings("unused")
private LocalDate getNextRestScheduleDate(LocalDate startDate, LoanApplicationTerms loanApplicationTerms,
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
LocalDate nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), startDate);
/*
* nextScheduleDate =
* this.scheduledDateGenerator.adjustRepaymentDate(nextScheduleDate,
* loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
*/
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
nextScheduleDate = this.scheduledDateGenerator.adjustRepaymentDate(nextScheduleDate, loanApplicationTerms, isHolidayEnabled,
holidays, workingDays);
}
return nextScheduleDate;
}
@@ -31,6 +31,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
public final class LoanApplicationTerms {
@@ -122,6 +123,8 @@ public final class LoanApplicationTerms {
private final CalendarInstance restCalendarInstance;
private final RecalculationFrequencyType recalculationFrequencyType;
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
final PeriodFrequencyType repaymentPeriodFrequencyType, final AmortizationMethod amortizationMethod,
@@ -139,13 +142,15 @@ public final class LoanApplicationTerms {
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
final CalendarInstance restCalendarInstance = null;
final RecalculationFrequencyType recalculationFrequencyType = null;
return new LoanApplicationTerms(currency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery,
repaymentPeriodFrequencyType, amortizationMethod, interestMethod, interestRatePerPeriod, interestRatePeriodFrequencyType,
annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney, expectedDisbursementDate,
repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, graceOnInterestPayment,
graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount, disbursementDatas,
maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance);
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
recalculationFrequencyType);
}
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
@@ -179,6 +184,7 @@ public final class LoanApplicationTerms {
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
final CalendarInstance restCalendarInstance = null;
final RecalculationFrequencyType recalculationFrequencyType = null;
return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
repaymentEvery, repaymentPeriodFrequencyType, amortizationMethod, interestMethod, interestRatePerPeriod,
@@ -187,7 +193,7 @@ public final class LoanApplicationTerms {
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(),
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
interestRecalculationCompoundingMethod, restCalendarInstance);
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType);
}
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
@@ -196,7 +202,8 @@ public final class LoanApplicationTerms {
final Money inArrearsTolerance, final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan,
final BigDecimal emiAmount, final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
final LoanInterestRecalculationDetails interestRecalculationDetails, final CalendarInstance restCalendarInstance) {
final LoanInterestRecalculationDetails interestRecalculationDetails, final CalendarInstance restCalendarInstance,
final RecalculationFrequencyType recalculationFrequencyType) {
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
@@ -233,7 +240,7 @@ public final class LoanApplicationTerms {
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(),
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
interestRecalculationCompoundingMethod, restCalendarInstance);
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType);
}
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
@@ -250,7 +257,8 @@ public final class LoanApplicationTerms {
applicationTerms.disbursementDatas, applicationTerms.maxOutstandingBalance, applicationTerms.emiAmountVariations,
applicationTerms.graceOnArrearsAgeing, applicationTerms.daysInMonthType, applicationTerms.daysInYearType,
applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance);
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance,
applicationTerms.recalculationFrequencyType);
}
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
@@ -266,7 +274,8 @@ public final class LoanApplicationTerms {
final BigDecimal maxOutstandingBalance, final List<LoanTermVariationsData> emiAmountVariations,
final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
final boolean isInterestRecalculationEnabled, final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod, final CalendarInstance restCalendarInstance) {
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod,
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType) {
this.currency = currency;
this.loanTermFrequency = loanTermFrequency;
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
@@ -304,6 +313,7 @@ public final class LoanApplicationTerms {
this.rescheduleStrategyMethod = rescheduleStrategyMethod;
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
this.restCalendarInstance = restCalendarInstance;
this.recalculationFrequencyType = recalculationFrequencyType;
}
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
@@ -1052,4 +1062,9 @@ public final class LoanApplicationTerms {
}
return numberOfPeriods;
}
public RecalculationFrequencyType getRecalculationFrequencyType() {
return this.recalculationFrequencyType;
}
}
@@ -41,7 +41,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
"graceOnInterestCharged", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale", "dateFormat", "charges",
"collateral", "syncDisbursementWithMeeting", "linkAccountId", LoanApiConstants.disbursementDataParameterName,
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.recalculationFrequencyTypeParameterName));
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanProductConstants.recalculationRestFrequencyDateParamName));
private final FromJsonHelper fromApiJsonHelper;
@@ -5,8 +5,6 @@
*/
package org.mifosplatform.portfolio.loanaccount.serialization;
import static org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants.recalculationFrequencyTypeParameterName;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -48,21 +46,26 @@ public final class LoanApplicationCommandFromApiJsonHelper {
final Set<String> supportedParameters = new HashSet<>(Arrays.asList("dateFormat", "locale", "id", "clientId", "groupId", "loanType",
"productId", "principal", "loanTermFrequency", "loanTermFrequencyType", "numberOfRepayments", "repaymentEvery",
"repaymentFrequencyType", "interestRatePerPeriod", "amortizationType", "interestType", "interestCalculationPeriodType",
"expectedDisbursementDate", "repaymentsStartingFromDate", "graceOnPrincipalPayment", "graceOnInterestPayment",
"expectedDisbursementDate", "repaymentsStartingFromDate", "graceOnPrincipalPayment",
"graceOnInterestPayment",
"graceOnInterestCharged",
"interestChargedFromDate", //
"submittedOnDate",
"submittedOnNote", //
"accountNo", "externalId", "fundId",
"accountNo",
"externalId",
"fundId",
"loanOfficerId", // optional
"loanPurposeId", "inArrearsTolerance", "charges",
"loanPurposeId",
"inArrearsTolerance",
"charges",
"collateral", // optional
"transactionProcessingStrategyId", // settings
"calendarId", // optional
"syncDisbursementWithMeeting",// optional
"linkAccountId", LoanApiConstants.disbursementDataParameterName, LoanApiConstants.emiAmountParameterName,
LoanApiConstants.maxOutstandingBalanceParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName,
LoanApiConstants.recalculationFrequencyTypeParameterName));
LoanProductConstants.recalculationRestFrequencyDateParamName));
private final FromJsonHelper fromApiJsonHelper;
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
@@ -360,13 +363,6 @@ public final class LoanApplicationCommandFromApiJsonHelper {
}
validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.recalculationFrequencyTypeParameterName, element)) {
final Integer recalculationFrequencyType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
LoanApiConstants.recalculationFrequencyTypeParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.recalculationFrequencyTypeParameterName).value(recalculationFrequencyType)
.notNull().inMinMaxRange(1, 5);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
@@ -452,7 +448,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
atLeastOneParameterPassedForUpdate = true;
final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(inArrearsToleranceParameterName,
element);
baseDataValidator.reset().parameter(inArrearsToleranceParameterName).value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
baseDataValidator.reset().parameter(inArrearsToleranceParameterName).value(inArrearsTolerance).ignoreIfNull()
.zeroOrPositiveAmount();
}
final String loanTermFrequencyParameterName = "loanTermFrequency";
@@ -839,11 +836,11 @@ public final class LoanApplicationCommandFromApiJsonHelper {
}
public void validateRecalcuationFrequencyType(final Integer recalculationFrequencyTypeValue) {
public void validateRecalcuationFrequency(final LocalDate recalculationFrequencyDate, final LocalDate expectedDisbursementDate) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
baseDataValidator.reset().parameter(recalculationFrequencyTypeParameterName).value(recalculationFrequencyTypeValue).notNull()
.inMinMaxRange(1, 5);
baseDataValidator.reset().parameter(LoanProductConstants.recalculationRestFrequencyDateParamName).value(recalculationFrequencyDate)
.notNull().validateDateBeforeOrEqual(expectedDisbursementDate);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
@@ -5,8 +5,6 @@
*/
package org.mifosplatform.portfolio.loanaccount.service;
import static org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants.recalculationFrequencyTypeParameterName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -70,6 +68,7 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleM
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationTransitionApiJsonValidator;
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
@@ -214,11 +213,13 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
if (loanProduct.isInterestRecalculationEnabled()) {
final Integer recalculationFrequencyTypeValue = command
.integerValueOfParameterNamed(recalculationFrequencyTypeParameterName);
this.fromApiJsonDeserializer.validateRecalcuationFrequencyType(recalculationFrequencyTypeValue);
createAndPersistCalendarInstanceForInterestRecalculation(newLoanApplication, recalculationFrequencyTypeValue);
final LocalDate recalculationFrequencyDate = this.fromJsonHelper.extractLocalDateNamed(
LoanProductConstants.recalculationRestFrequencyDateParamName, command.parsedJson());
if (!newLoanApplication.loanInterestRecalculationDetails().getRestFrequencyType().isSameAsRepayment()) {
this.fromApiJsonDeserializer.validateRecalcuationFrequency(recalculationFrequencyDate,
newLoanApplication.getExpectedDisbursedOnLocalDate());
}
createAndPersistCalendarInstanceForInterestRecalculation(newLoanApplication);
}
if (newLoanApplication.isAccountNumberRequiresAutoGeneration()) {
@@ -271,33 +272,30 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
}
private void createAndPersistCalendarInstanceForInterestRecalculation(final Loan loan, final Integer recalculationFrequencyTypeValue) {
private void createAndPersistCalendarInstanceForInterestRecalculation(final Loan loan) {
final LocalDate calendarStartDate = loan.getDisbursementDate();
LocalDate calendarStartDate = loan.loanInterestRecalculationDetails().getRestFrequencyLocalDate();
if (calendarStartDate == null) {
calendarStartDate = loan.getExpectedDisbursedOnLocalDate();
}
final Integer repeatsOnDay = calendarStartDate.getDayOfWeek();
final RecalculationFrequencyType recalculationFrequencyType = RecalculationFrequencyType.fromInt(recalculationFrequencyTypeValue);
final RecalculationFrequencyType recalculationFrequencyType = loan.loanInterestRecalculationDetails().getRestFrequencyType();
Integer frequency = 1;
Integer frequency = loan.loanInterestRecalculationDetails().getRestInterval();
CalendarFrequencyType calendarFrequencyType = CalendarFrequencyType.INVALID;
switch (recalculationFrequencyType) {
case DAILY:
frequency = 1;
calendarFrequencyType = CalendarFrequencyType.DAILY;
break;
case FORTNIGHTLY:
frequency = 2;
calendarFrequencyType = CalendarFrequencyType.WEEKLY;
break;
case MONTHLY:
frequency = 1;
calendarFrequencyType = CalendarFrequencyType.MONTHLY;
break;
case SAME_AS_REPAYMENT_PERIOD:
frequency = loan.repaymentScheduleDetail().getRepayEvery();
calendarFrequencyType = CalendarFrequencyType.from(loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType());
calendarStartDate = loan.getExpectedDisbursedOnLocalDate();
break;
case WEEKLY:
frequency = 1;
calendarFrequencyType = CalendarFrequencyType.WEEKLY;
break;
default:
@@ -313,35 +311,32 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
private void updateCalendarDetailsForInterestRecalculation(final CalendarInstance calendarInstance, final Loan loan,
final Integer recalculationFrequencyTypeValue) {
private void updateCalendarDetailsForInterestRecalculation(final CalendarInstance calendarInstance, final Loan loan) {
Calendar interestRecalculationRecurrings = calendarInstance.getCalendar();
final LocalDate calendarStartDate = loan.getDisbursementDate();
LocalDate calendarStartDate = loan.loanInterestRecalculationDetails().getRestFrequencyLocalDate();
if (calendarStartDate == null) {
calendarStartDate = loan.getExpectedDisbursedOnLocalDate();
}
final Integer repeatsOnDay = calendarStartDate.getDayOfWeek();
final RecalculationFrequencyType recalculationFrequencyType = RecalculationFrequencyType.fromInt(recalculationFrequencyTypeValue);
final RecalculationFrequencyType recalculationFrequencyType = loan.loanInterestRecalculationDetails().getRestFrequencyType();
Integer frequency = 1;
Integer frequency = loan.loanInterestRecalculationDetails().getRestInterval();
CalendarFrequencyType calendarFrequencyType = CalendarFrequencyType.INVALID;
switch (recalculationFrequencyType) {
case DAILY:
frequency = 1;
calendarFrequencyType = CalendarFrequencyType.DAILY;
break;
case FORTNIGHTLY:
frequency = 2;
calendarFrequencyType = CalendarFrequencyType.WEEKLY;
break;
case MONTHLY:
frequency = 1;
calendarFrequencyType = CalendarFrequencyType.MONTHLY;
break;
case SAME_AS_REPAYMENT_PERIOD:
calendarStartDate = loan.getExpectedDisbursedOnLocalDate();
frequency = loan.repaymentScheduleDetail().getRepayEvery();
calendarFrequencyType = CalendarFrequencyType.from(loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType());
break;
case WEEKLY:
frequency = 1;
calendarFrequencyType = CalendarFrequencyType.WEEKLY;
break;
default:
@@ -616,26 +611,29 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
// updating loan interest recalculation details throwing null
// pointer exception after saveAndFlush
// http://stackoverflow.com/questions/17151757/hibernate-cascade-update-gives-null-pointer/17334374#17334374
existingLoanApplication.updateLoanInterestRecalculationSettings();
this.loanRepository.save(existingLoanApplication);
if (productRelatedDetail.isInterestRecalculationEnabled()) {
final Integer recalculationFrequencyTypeValue = command
.integerValueOfParameterNamed(recalculationFrequencyTypeParameterName);
this.fromApiJsonDeserializer.validateRecalcuationFrequencyType(recalculationFrequencyTypeValue);
LocalDate recalculationFrequencyDate = existingLoanApplication.loanProduct().getProductInterestRecalculationDetails()
.getRestFrequencyLocalDate();
if (this.fromJsonHelper.parameterExists(LoanProductConstants.recalculationRestFrequencyDateParamName, command.parsedJson())) {
recalculationFrequencyDate = this.fromJsonHelper.extractLocalDateNamed(
LoanProductConstants.recalculationRestFrequencyDateParamName, command.parsedJson());
if (!existingLoanApplication.loanInterestRecalculationDetails().getRestFrequencyType().isSameAsRepayment()) {
this.fromApiJsonDeserializer.validateRecalcuationFrequency(recalculationFrequencyDate,
existingLoanApplication.getExpectedDisbursedOnLocalDate());
}
}
CalendarInstance calendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId(
existingLoanApplication.loanInterestRecalculationDetailId(),
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue(), CalendarType.COLLECTION.getValue());
if (calendarInstance == null) {
createAndPersistCalendarInstanceForInterestRecalculation(existingLoanApplication, recalculationFrequencyTypeValue);
createAndPersistCalendarInstanceForInterestRecalculation(existingLoanApplication);
} else {
updateCalendarDetailsForInterestRecalculation(calendarInstance, existingLoanApplication,
recalculationFrequencyTypeValue);
updateCalendarDetailsForInterestRecalculation(calendarInstance, existingLoanApplication);
}
}
@@ -61,6 +61,7 @@ import org.mifosplatform.portfolio.loanaccount.exception.MultiDisbursementDataRe
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
@@ -260,6 +261,8 @@ public class LoanAssembler {
loanDisbursementDetails.updateLoan(loanApplication);
}
}
final LocalDate recalculationRestFrequencyDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanProductConstants.recalculationRestFrequencyDateParamName, element);
final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element);
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
@@ -271,7 +274,8 @@ public class LoanAssembler {
final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms,
isHolidayEnabled, holidays, workingDays, element);
loanApplication.loanApplicationSubmittal(currentUser, loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine(),
submittedOnDate, externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay);
submittedOnDate, externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay,
recalculationRestFrequencyDate);
return loanApplication;
}
@@ -533,7 +533,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.loan_counter as loanCounter, l.loan_product_counter as loanProductCounter,"
+ " l.is_npa as isNPA, l.days_in_month_enum as daysInMonth, l.days_in_year_enum as daysInYear, "
+ " l.interest_recalculation_enabled as isInterestRecalculationEnabled, "
+ " lir.id as lirId, lir.loan_id as loanId, lir.compound_type_enum as compoundType, lir.reschedule_strategy_enum as rescheduleStrategy "
+ " lir.id as lirId, lir.loan_id as loanId, lir.compound_type_enum as compoundType, lir.reschedule_strategy_enum as rescheduleStrategy, "
+ " lir.rest_frequency_type_enum as restFrequencyEnum, lir.rest_frequency_interval as restFrequencyInterval, "
+ " lir.rest_freqency_date as restFrequencyDate "
+ " from m_loan l" //
+ " join m_product_loan lp on lp.id = l.product_id" //
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id "
@@ -775,9 +777,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final int rescheduleStrategyEnumValue = JdbcSupport.getInteger(rs, "rescheduleStrategy");
final EnumOptionData rescheduleStrategyType = LoanEnumerations.rescheduleStrategyType(rescheduleStrategyEnumValue);
final CalendarData calendarData = null;
final int restFrequencyEnumValue = JdbcSupport.getInteger(rs, "restFrequencyEnum");
final EnumOptionData restFrequencyType = LoanEnumerations.interestRecalculationFrequencyType(restFrequencyEnumValue);
final int restFrequencyInterval = JdbcSupport.getInteger(rs, "restFrequencyInterval");
final LocalDate restFrequencyDate = JdbcSupport.getLocalDate(rs, "restFrequencyDate");
interestRecalculationData = new LoanInterestRecalculationData(lprId, productId, interestRecalculationCompoundingType,
rescheduleStrategyType, calendarData);
rescheduleStrategyType, calendarData, restFrequencyType, restFrequencyInterval, restFrequencyDate);
}
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupData,
@@ -1220,13 +1226,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
}
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions = this.loanDropdownReadPlatformService
.retrieveInterestRecalculationFrequencyTypeOptions();
return LoanAccountData.loanProductWithTemplateDefaults(loanProduct, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, loanPurposeOptions, loanCollateralOptions,
loanCycleCounter, interestRecalculationFrequencyTypeOptions);
loanCycleCounter);
}
@Override
@@ -68,5 +68,8 @@ public interface LoanProductConstants {
public static final String daysInMonthTypeParameterName = "daysInMonthType";
public static final String interestRecalculationCompoundingMethodParameterName = "interestRecalculationCompoundingMethod";
public static final String rescheduleStrategyMethodParameterName = "rescheduleStrategyMethod";
public static final String recalculationRestFrequencyTypeParameterName = "recalculationRestFrequencyType";
public static final String recalculationRestFrequencyIntervalParameterName = "recalculationRestFrequencyInterval";
public static final String recalculationRestFrequencyDateParamName = "recalculationRestFrequencyDate";
}
@@ -76,8 +76,8 @@ public class LoanProductsApiResource {
"transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions",
"accountingMappingOptions"));
private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName",
"restrictedProducts", "allowedProducts", "productOptions"));
private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
"allowedProducts", "productOptions"));
private final String resourceNameForPermissions = "LOANPRODUCT";
@@ -277,12 +277,14 @@ public class LoanProductsApiResource {
final List<EnumOptionData> interestRecalculationCompoundingTypeOptions = dropdownReadPlatformService
.retrieveInterestRecalculationCompoundingTypeOptions();
final List<EnumOptionData> rescheduleStrategyTypeOptions = dropdownReadPlatformService.retrieveRescheduleStrategyTypeOptions();
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions = dropdownReadPlatformService
.retrieveInterestRecalculationFrequencyTypeOptions();
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions,
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions,
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions,
accountingRuleTypeOptions, loanCycleValueConditionTypeOptions, daysInMonthTypeOptions, daysInYearTypeOptions,
interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions);
interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions);
}
}
@@ -32,6 +32,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.springframework.util.CollectionUtils;
/**
* Immutable data object to represent loan products.
@@ -126,6 +127,8 @@ public class LoanProductData {
@SuppressWarnings("unused")
private final List<EnumOptionData> rescheduleStrategyTypeOptions;
private final List<EnumOptionData> interestRecalculationFrequencyTypeOptions;
private final Boolean multiDisburseLoan;
private final Integer maxTrancheCount;
private final BigDecimal outstandingLoanBalance;
@@ -427,6 +430,7 @@ public class LoanProductData {
this.daysInYearTypeOptions = null;
this.interestRecalculationCompoundingTypeOptions = null;
this.rescheduleStrategyTypeOptions = null;
this.interestRecalculationFrequencyTypeOptions = null;
}
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
@@ -438,7 +442,7 @@ public class LoanProductData {
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
final List<EnumOptionData> valueConditionTypeOptions, final List<EnumOptionData> daysInMonthTypeOptions,
final List<EnumOptionData> daysInYearTypeOptions, final List<EnumOptionData> interestRecalculationCompoundingTypeOptions,
final List<EnumOptionData> rescheduleStrategyTypeOptions) {
final List<EnumOptionData> rescheduleStrategyTypeOptions, final List<EnumOptionData> interestRecalculationFrequencyTypeOptions) {
this.id = productData.id;
this.name = productData.name;
this.shortName = productData.shortName;
@@ -526,6 +530,12 @@ public class LoanProductData {
this.interestRecalculationCompoundingTypeOptions = interestRecalculationCompoundingTypeOptions;
this.rescheduleStrategyTypeOptions = rescheduleStrategyTypeOptions;
if (CollectionUtils.isEmpty(interestRecalculationFrequencyTypeOptions)) {
this.interestRecalculationFrequencyTypeOptions = null;
} else {
this.interestRecalculationFrequencyTypeOptions = interestRecalculationFrequencyTypeOptions;
}
}
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
@@ -747,7 +757,8 @@ public class LoanProductData {
final Long loanId = null;
final CalendarData calendarData = null;
return new LoanInterestRecalculationData(id, loanId, getInterestRecalculationCompoundingType(), getRescheduleStrategyType(),
calendarData);
calendarData, getRecalculationRestFrequencyType(), getRecalculationRestFrequencyInterval(),
getRecalculationRestFrequencyDate());
}
private EnumOptionData getRescheduleStrategyType() {
@@ -759,4 +770,19 @@ public class LoanProductData {
if (isInterestRecalculationEnabled()) { return this.interestRecalculationData.getInterestRecalculationCompoundingType(); }
return null;
}
private LocalDate getRecalculationRestFrequencyDate() {
if (isInterestRecalculationEnabled()) { return this.interestRecalculationData.getRecalculationRestFrequencyDate(); }
return null;
}
public EnumOptionData getRecalculationRestFrequencyType() {
if (isInterestRecalculationEnabled()) { return this.interestRecalculationData.getRecalculationRestFrequencyType(); }
return null;
}
public Integer getRecalculationRestFrequencyInterval() {
if (isInterestRecalculationEnabled()) { return this.interestRecalculationData.getRecalculationRestFrequencyInterval(); }
return null;
}
}
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.loanproduct.data;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestRecalculationCompoundingType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.rescheduleStrategyType;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
@@ -20,13 +21,21 @@ public class LoanProductInterestRecalculationData {
private final Long productId;
private final EnumOptionData interestRecalculationCompoundingType;
private final EnumOptionData rescheduleStrategyType;
private final EnumOptionData recalculationRestFrequencyType;
private final Integer recalculationRestFrequencyInterval;
private final LocalDate recalculationRestFrequencyDate;
public LoanProductInterestRecalculationData(final Long id, final Long productId,
final EnumOptionData interestRecalculationCompoundingType, final EnumOptionData rescheduleStrategyType) {
final EnumOptionData interestRecalculationCompoundingType, final EnumOptionData rescheduleStrategyType,
final EnumOptionData recalculationRestFrequencyType, final Integer recalculationRestFrequencyInterval,
final LocalDate recalculationRestFrequencyDate) {
this.id = id;
this.productId = productId;
this.interestRecalculationCompoundingType = interestRecalculationCompoundingType;
this.rescheduleStrategyType = rescheduleStrategyType;
this.recalculationRestFrequencyType = recalculationRestFrequencyType;
this.recalculationRestFrequencyInterval = recalculationRestFrequencyInterval;
this.recalculationRestFrequencyDate = recalculationRestFrequencyDate;
}
public static LoanProductInterestRecalculationData sensibleDefaultsForNewLoanProductCreation() {
@@ -34,7 +43,11 @@ public class LoanProductInterestRecalculationData {
final Long productId = null;
final EnumOptionData interestRecalculationCompoundingType = interestRecalculationCompoundingType(InterestRecalculationCompoundingMethod.NONE);
final EnumOptionData rescheduleStrategyType = rescheduleStrategyType(LoanRescheduleStrategyMethod.REDUCE_EMI_AMOUNT);
return new LoanProductInterestRecalculationData(id, productId, interestRecalculationCompoundingType, rescheduleStrategyType);
final EnumOptionData recalculationRestFrequencyType = null;
final Integer recalculationRestFrequencyInterval = null;
final LocalDate recalculationRestFrequencyDate = null;
return new LoanProductInterestRecalculationData(id, productId, interestRecalculationCompoundingType, rescheduleStrategyType,
recalculationRestFrequencyType, recalculationRestFrequencyInterval, recalculationRestFrequencyDate);
}
public EnumOptionData getInterestRecalculationCompoundingType() {
@@ -45,4 +58,16 @@ public class LoanProductInterestRecalculationData {
return this.rescheduleStrategyType;
}
public LocalDate getRecalculationRestFrequencyDate() {
return this.recalculationRestFrequencyDate;
}
public EnumOptionData getRecalculationRestFrequencyType() {
return this.recalculationRestFrequencyType;
}
public Integer getRecalculationRestFrequencyInterval() {
return this.recalculationRestFrequencyInterval;
}
}
@@ -43,7 +43,6 @@ 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.fund.domain.Fund;
import org.mifosplatform.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -906,21 +905,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
return this.loanProductRelatedDetail.fetchDaysInYearType();
}
public LoanInterestRecalculationDetails copyInterestRecalculationSettings() {
LoanInterestRecalculationDetails loanInterestRecalculationDetails = null;
if (this.isInterestRecalculationEnabled()) {
loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom(
this.productInterestRecalculationDetails.getInterestRecalculationCompoundingMethod(),
this.productInterestRecalculationDetails.getRescheduleStrategyMethod());
}
return loanInterestRecalculationDetails;
}
public LoanInterestRecalculationDetails updateLoanInterestRecalculationDetails(
LoanInterestRecalculationDetails loanInterestRecalculationDetails) {
loanInterestRecalculationDetails.update(this.productInterestRecalculationDetails.getInterestRecalculationCompoundingMethod(),
this.productInterestRecalculationDetails.getRescheduleStrategyMethod());
return loanInterestRecalculationDetails;
public LoanProductInterestRecalculationDetails getProductInterestRecalculationDetails() {
return this.productInterestRecalculationDetails;
}
}
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.loanproduct.domain;
import java.util.Date;
import java.util.Map;
import javax.persistence.Column;
@@ -12,7 +13,10 @@ import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -27,6 +31,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
@Table(name = "m_product_loan_recalculation_details")
public class LoanProductInterestRecalculationDetails extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@OneToOne
@JoinColumn(name = "product_id", nullable = false)
private LoanProduct loanProduct;
@@ -43,6 +48,16 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable
@Column(name = "reschedule_strategy_enum", nullable = false)
private Integer rescheduleStrategyMethod;
@Column(name = "rest_frequency_type_enum", nullable = false)
private Integer restFrequencyType;
@Column(name = "rest_frequency_interval", nullable = false)
private Integer restInterval;
@Temporal(TemporalType.DATE)
@Column(name = "rest_freqency_date")
private Date restFrequencyDate;
protected LoanProductInterestRecalculationDetails() {
//
}
@@ -55,13 +70,34 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable
final Integer loanRescheduleStrategyMethod = LoanRescheduleStrategyMethod.fromInt(
command.integerValueOfParameterNamed(LoanProductConstants.rescheduleStrategyMethodParameterName)).getValue();
return new LoanProductInterestRecalculationDetails(interestRecalculationCompoundingMethod, loanRescheduleStrategyMethod);
final Integer recurrenceFrequency = command
.integerValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyTypeParameterName);
final LocalDate recurrenceOnLocalDate = command
.localDateValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyDateParamName);
Integer recurrenceInterval = command
.integerValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyIntervalParameterName);
RecalculationFrequencyType frequencyType = RecalculationFrequencyType.fromInt(recurrenceFrequency);
Date recurrenceOnDate = null;
if (recurrenceOnLocalDate != null) {
if (frequencyType.isSameAsRepayment()) {
recurrenceInterval = 0;
} else {
recurrenceOnDate = recurrenceOnLocalDate.toDate();
}
}
return new LoanProductInterestRecalculationDetails(interestRecalculationCompoundingMethod, loanRescheduleStrategyMethod,
recurrenceFrequency, recurrenceInterval, recurrenceOnDate);
}
private LoanProductInterestRecalculationDetails(final Integer interestRecalculationCompoundingMethod,
final Integer rescheduleStrategyMethod) {
final Integer rescheduleStrategyMethod, final Integer restFrequencyType, final Integer restInterval,
final Date restFrequencyDate) {
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
this.rescheduleStrategyMethod = rescheduleStrategyMethod;
this.restFrequencyType = restFrequencyType;
this.restInterval = restInterval;
this.restFrequencyDate = restFrequencyDate;
}
public void updateProduct(final LoanProduct loanProduct) {
@@ -94,5 +130,60 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable
actualChanges.put("locale", localeAsInput);
this.rescheduleStrategyMethod = LoanRescheduleStrategyMethod.fromInt(newValue).getValue();
}
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.recalculationRestFrequencyTypeParameterName,
this.restFrequencyType)) {
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyTypeParameterName);
actualChanges.put(LoanProductConstants.recalculationRestFrequencyTypeParameterName, newValue);
actualChanges.put("locale", localeAsInput);
this.restFrequencyType = RecalculationFrequencyType.fromInt(newValue).getValue();
}
RecalculationFrequencyType frequencyType = RecalculationFrequencyType.fromInt(this.restFrequencyType);
if (frequencyType.isSameAsRepayment()) {
this.restInterval = 0;
this.restFrequencyDate = null;
} else {
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.recalculationRestFrequencyIntervalParameterName,
this.restInterval)) {
Integer newValue = command
.integerValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyIntervalParameterName);
if (frequencyType.isSameAsRepayment()) {
newValue = 0;
}
actualChanges.put(LoanProductConstants.recalculationRestFrequencyIntervalParameterName, newValue);
actualChanges.put("locale", localeAsInput);
this.restInterval = newValue;
}
if (command.isChangeInLocalDateParameterNamed(LoanProductConstants.recalculationRestFrequencyDateParamName,
getRestFrequencyLocalDate())) {
final LocalDate newValue = command
.localDateValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyDateParamName);
Date recurrenceOnDate = null;
if (newValue != null) {
if (!frequencyType.isSameAsRepayment()) {
recurrenceOnDate = newValue.toDate();
}
}
actualChanges.put(LoanProductConstants.recalculationRestFrequencyDateParamName, newValue);
this.restFrequencyDate = recurrenceOnDate;
}
}
}
public LocalDate getRestFrequencyLocalDate() {
LocalDate recurrenceOnLocalDate = null;
if (this.restFrequencyDate != null) {
recurrenceOnLocalDate = new LocalDate(this.restFrequencyDate);
}
return recurrenceOnLocalDate;
}
public RecalculationFrequencyType getRestFrequencyType() {
return RecalculationFrequencyType.fromInt(this.restFrequencyType);
}
public Integer getRestInterval() {
return this.restInterval;
}
}
@@ -8,13 +8,14 @@ package org.mifosplatform.portfolio.loanproduct.domain;
import java.util.HashMap;
import java.util.Map;
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
public enum RecalculationFrequencyType {
INVALID(0, "interestRecalculationFrequencyType.invalid"), //
SAME_AS_REPAYMENT_PERIOD(1, "interestRecalculationFrequencyType.same.as.repayment.period"), //
DAILY(2, "interestRecalculationFrequencyType.daily"), //
WEEKLY(3, "interestRecalculationFrequencyType.weekly"), //
FORTNIGHTLY(4, "interestRecalculationFrequencyType.fortnightly"), //
MONTHLY(5, "interestRecalculationFrequencyType.monthly");
MONTHLY(4, "interestRecalculationFrequencyType.monthly");
private final Integer value;
private final String code;
@@ -44,4 +45,20 @@ public enum RecalculationFrequencyType {
public String getCode() {
return this.code;
}
public boolean isSameAsRepayment() {
return this.value.equals(RecalculationFrequencyType.SAME_AS_REPAYMENT_PERIOD.getValue());
}
public boolean isDaily() {
return this.value.equals(RecalculationFrequencyType.DAILY.getValue());
}
public boolean isWeekly() {
return this.value.equals(RecalculationFrequencyType.WEEKLY.getValue());
}
public boolean isMonthly() {
return this.value.equals(RecalculationFrequencyType.MONTHLY.getValue());
}
}
@@ -16,6 +16,7 @@ import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
@@ -27,6 +28,7 @@ import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -41,8 +43,8 @@ public final class LoanProductDataValidator {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("locale", "dateFormat", "name", "description",
"fundId", "currencyCode", "digitsAfterDecimal", "inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery",
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("locale", "dateFormat", "name", "description", "fundId",
"currencyCode", "digitsAfterDecimal", "inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery",
"numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod",
"minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
"interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment",
@@ -54,7 +56,8 @@ public final class LoanProductDataValidator {
LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), LoanProductConstants.useBorrowerCycleParameterName,
LoanProductConstants.principalVariationsForBorrowerCycleParameterName,
LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName,
@@ -64,7 +67,10 @@ public final class LoanProductDataValidator {
LoanProductConstants.overdueDaysForNPAParameterName, LoanProductConstants.isInterestRecalculationEnabledParameterName,
LoanProductConstants.daysInYearTypeParameterName, LoanProductConstants.daysInMonthTypeParameterName,
LoanProductConstants.rescheduleStrategyMethodParameterName,
LoanProductConstants.interestRecalculationCompoundingMethodParameterName));
LoanProductConstants.interestRecalculationCompoundingMethodParameterName,
LoanProductConstants.recalculationRestFrequencyDateParamName,
LoanProductConstants.recalculationRestFrequencyIntervalParameterName,
LoanProductConstants.recalculationRestFrequencyTypeParameterName));
private final FromJsonHelper fromApiJsonHelper;
@@ -129,18 +135,18 @@ public final class LoanProductDataValidator {
baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).ignoreIfNull().positiveAmount();
}
if (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) != -1 ) {
if (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) != -1) {
if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) != -1) {
baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).notLessThanMin(minPrincipalAmount);
if (minPrincipalAmount.compareTo(maxPrincipalAmount) <= 0 && principal != null ) {
if (minPrincipalAmount.compareTo(maxPrincipalAmount) <= 0 && principal != null) {
baseDataValidator.reset().parameter("principal").value(principal)
.inMinAndMaxAmountRange(minPrincipalAmount, maxPrincipalAmount);
}
} else if(principal != null) {
} else if (principal != null) {
baseDataValidator.reset().parameter("principal").value(principal).notGreaterThanMax(maxPrincipalAmount);
}
} else if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) != -1 && principal !=null) {
} else if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) != -1 && principal != null) {
baseDataValidator.reset().parameter("principal").value(principal).notLessThanMin(minPrincipalAmount);
}
@@ -291,7 +297,7 @@ public final class LoanProductDataValidator {
if (isInterestRecalculationEnabled != null) {
if (isInterestRecalculationEnabled.booleanValue()) {
validateInterestRecalculationParams(element, baseDataValidator);
validateInterestRecalculationParams(element, baseDataValidator, null);
}
}
@@ -333,8 +339,8 @@ public final class LoanProductDataValidator {
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue()).value(incomeFromRecoveryAccountId)
.notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue())
.value(incomeFromRecoveryAccountId).notNull().integerGreaterThanZero();
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
@@ -411,23 +417,67 @@ public final class LoanProductDataValidator {
}
}
private void validateInterestRecalculationParams(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
private void validateInterestRecalculationParams(final JsonElement element, final DataValidatorBuilder baseDataValidator,
final LoanProduct loanProduct) {
/**
* { @link InterestRecalculationCompoundingMethod }
*/
final Integer interestRecalculationCompoundingMethod = this.fromApiJsonHelper.extractIntegerNamed(
LoanProductConstants.interestRecalculationCompoundingMethodParameterName, element, Locale.getDefault());
baseDataValidator.reset().parameter(LoanProductConstants.interestRecalculationCompoundingMethodParameterName)
.value(interestRecalculationCompoundingMethod).notNull().inMinMaxRange(0, 3);
if (loanProduct == null
|| this.fromApiJsonHelper
.parameterExists(LoanProductConstants.interestRecalculationCompoundingMethodParameterName, element)) {
final Integer interestRecalculationCompoundingMethod = this.fromApiJsonHelper.extractIntegerNamed(
LoanProductConstants.interestRecalculationCompoundingMethodParameterName, element, Locale.getDefault());
baseDataValidator.reset().parameter(LoanProductConstants.interestRecalculationCompoundingMethodParameterName)
.value(interestRecalculationCompoundingMethod).notNull().inMinMaxRange(0, 3);
}
/**
* { @link LoanRescheduleStrategyMethod }
*/
final Integer rescheduleStrategyMethod = this.fromApiJsonHelper.extractIntegerNamed(
LoanProductConstants.rescheduleStrategyMethodParameterName, element, Locale.getDefault());
baseDataValidator.reset().parameter(LoanProductConstants.rescheduleStrategyMethodParameterName).value(rescheduleStrategyMethod)
.notNull().inMinMaxRange(1, 3);
if (loanProduct == null
|| this.fromApiJsonHelper.parameterExists(LoanProductConstants.rescheduleStrategyMethodParameterName, element)) {
final Integer rescheduleStrategyMethod = this.fromApiJsonHelper.extractIntegerNamed(
LoanProductConstants.rescheduleStrategyMethodParameterName, element, Locale.getDefault());
baseDataValidator.reset().parameter(LoanProductConstants.rescheduleStrategyMethodParameterName).value(rescheduleStrategyMethod)
.notNull().inMinMaxRange(1, 3);
}
RecalculationFrequencyType frequencyType = null;
if (loanProduct == null
|| this.fromApiJsonHelper.parameterExists(LoanProductConstants.recalculationRestFrequencyTypeParameterName, element)) {
final Integer recalculationRestFrequencyType = this.fromApiJsonHelper.extractIntegerNamed(
LoanProductConstants.recalculationRestFrequencyTypeParameterName, element, Locale.getDefault());
baseDataValidator.reset().parameter(LoanProductConstants.recalculationRestFrequencyTypeParameterName)
.value(recalculationRestFrequencyType).notNull().inMinMaxRange(1, 4);
frequencyType = RecalculationFrequencyType.fromInt(recalculationRestFrequencyType);
}
if (frequencyType == null) {
if (loanProduct == null) {
frequencyType = RecalculationFrequencyType.INVALID;
} else {
frequencyType = loanProduct.getProductInterestRecalculationDetails().getRestFrequencyType();
}
}
if (!frequencyType.isSameAsRepayment()) {
if (loanProduct == null
|| this.fromApiJsonHelper.parameterExists(LoanProductConstants.recalculationRestFrequencyDateParamName, element)) {
final LocalDate recurrenceOnLocalDate = this.fromApiJsonHelper.extractLocalDateNamed(
LoanProductConstants.recalculationRestFrequencyDateParamName, element);
baseDataValidator.reset().parameter(LoanProductConstants.recalculationRestFrequencyDateParamName)
.value(recurrenceOnLocalDate).notNull();
}
if (loanProduct == null
|| this.fromApiJsonHelper
.parameterExists(LoanProductConstants.recalculationRestFrequencyIntervalParameterName, element)) {
final Integer recurrenceInterval = this.fromApiJsonHelper.extractIntegerNamed(
LoanProductConstants.recalculationRestFrequencyIntervalParameterName, element, Locale.getDefault());
baseDataValidator.reset().parameter(LoanProductConstants.recalculationRestFrequencyIntervalParameterName)
.value(recurrenceInterval).notNull();
}
}
}
@@ -654,7 +704,7 @@ public final class LoanProductDataValidator {
if (isInterestRecalculationEnabled != null) {
if (isInterestRecalculationEnabled.booleanValue()) {
validateInterestRecalculationParams(element, baseDataValidator);
validateInterestRecalculationParams(element, baseDataValidator, loanProduct);
}
}
}
@@ -693,8 +743,8 @@ public final class LoanProductDataValidator {
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue()).value(incomeFromRecoveryAccountId)
.ignoreIfNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue())
.value(incomeFromRecoveryAccountId).ignoreIfNull().integerGreaterThanZero();
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
@@ -141,12 +141,11 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat
@Override
public List<EnumOptionData> retrieveInterestRecalculationFrequencyTypeOptions() {
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions = Arrays.asList(
interestRecalculationFrequencyType(RecalculationFrequencyType.SAME_AS_REPAYMENT_PERIOD),
interestRecalculationFrequencyType(RecalculationFrequencyType.DAILY),
interestRecalculationFrequencyType(RecalculationFrequencyType.WEEKLY),
interestRecalculationFrequencyType(RecalculationFrequencyType.FORTNIGHTLY),
interestRecalculationFrequencyType(RecalculationFrequencyType.MONTHLY));
return interestRecalculationFrequencyTypeOptions;
}
@@ -526,10 +526,6 @@ public class LoanEnumerations {
optionData = new EnumOptionData(RecalculationFrequencyType.DAILY.getValue().longValue(),
RecalculationFrequencyType.DAILY.getCode(), "Daily");
break;
case FORTNIGHTLY:
optionData = new EnumOptionData(RecalculationFrequencyType.FORTNIGHTLY.getValue().longValue(),
RecalculationFrequencyType.FORTNIGHTLY.getCode(), "Fortnightly");
break;
case MONTHLY:
optionData = new EnumOptionData(RecalculationFrequencyType.MONTHLY.getValue().longValue(),
RecalculationFrequencyType.MONTHLY.getCode(), "Monthly");
@@ -125,6 +125,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, "
+ "lp.days_in_month_enum as daysInMonth, lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as isInterestRecalculationEnabled, "
+ "lpr.id as lprId, lpr.product_id as productId, lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as rescheduleStrategy, "
+ "lpr.rest_frequency_type_enum as restFrequencyEnum, lpr.rest_frequency_interval as restFrequencyInterval, "
+ "lpr.rest_freqency_date as restFrequencyDate, "
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId "
+ " from m_product_loan lp "
+ " left join m_fund f on f.id = lp.fund_id "
@@ -242,9 +244,14 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
.interestRecalculationCompoundingType(compoundTypeEnumValue);
final int rescheduleStrategyEnumValue = JdbcSupport.getInteger(rs, "rescheduleStrategy");
final EnumOptionData rescheduleStrategyType = LoanEnumerations.rescheduleStrategyType(rescheduleStrategyEnumValue);
final int restFrequencyEnumValue = JdbcSupport.getInteger(rs, "restFrequencyEnum");
final EnumOptionData restFrequencyType = LoanEnumerations.interestRecalculationFrequencyType(restFrequencyEnumValue);
final int restFrequencyInterval = JdbcSupport.getInteger(rs, "restFrequencyInterval");
final LocalDate restFrequencyDate = JdbcSupport.getLocalDate(rs, "restFrequencyDate");
interestRecalculationData = new LoanProductInterestRecalculationData(lprId, productId,
interestRecalculationCompoundingType, rescheduleStrategyType);
interestRecalculationCompoundingType, rescheduleStrategyType, restFrequencyType, restFrequencyInterval,
restFrequencyDate);
}
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,