MIFOSX-1419 : moved capturing of rest frequency to product

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