MIFOSX-282:added min and max constraints for Interest rate and number of repayments

This commit is contained in:
ashok-conflux
2013-04-05 20:24:40 +05:30
parent c28ad6b18c
commit 2446a0f05e
16 changed files with 687 additions and 102 deletions
@@ -0,0 +1,37 @@
ALTER TABLE `m_product_loan`
ADD COLUMN `min_nominal_interest_rate_per_period` DECIMAL(19,6) NOT NULL AFTER `nominal_interest_rate_per_period`,
ADD COLUMN `max_nominal_interest_rate_per_period` DECIMAL(19,6) NOT NULL AFTER `min_nominal_interest_rate_per_period`,
ADD COLUMN `min_number_of_repayments` SMALLINT(5) NOT NULL AFTER `number_of_repayments`,
ADD COLUMN `max_number_of_repayments` SMALLINT(5) NOT NULL AFTER `min_number_of_repayments`;
ALTER TABLE `m_loan`
ADD COLUMN `min_nominal_interest_rate_per_period` DECIMAL(19,6) NOT NULL AFTER `nominal_interest_rate_per_period`,
ADD COLUMN `max_nominal_interest_rate_per_period` DECIMAL(19,6) NOT NULL AFTER `min_nominal_interest_rate_per_period`,
ADD COLUMN `min_number_of_repayments` SMALLINT(5) NOT NULL AFTER `number_of_repayments`,
ADD COLUMN `max_number_of_repayments` SMALLINT(5) NOT NULL AFTER `min_number_of_repayments`;
ALTER TABLE `m_loan`
ALTER `min_principal_amount` DROP DEFAULT,
ALTER `max_principal_amount` DROP DEFAULT;
ALTER TABLE `m_loan`
CHANGE COLUMN `min_principal_amount` `min_principal_amount` DECIMAL(19,6) NULL AFTER `principal_amount`,
CHANGE COLUMN `max_principal_amount` `max_principal_amount` DECIMAL(19,6) NULL AFTER `min_principal_amount`,
CHANGE COLUMN `min_nominal_interest_rate_per_period` `min_nominal_interest_rate_per_period` DECIMAL(19,6) NULL AFTER `nominal_interest_rate_per_period`,
CHANGE COLUMN `max_nominal_interest_rate_per_period` `max_nominal_interest_rate_per_period` DECIMAL(19,6) NULL AFTER `min_nominal_interest_rate_per_period`,
CHANGE COLUMN `min_number_of_repayments` `min_number_of_repayments` SMALLINT(5) NULL AFTER `number_of_repayments`,
CHANGE COLUMN `max_number_of_repayments` `max_number_of_repayments` SMALLINT(5) NULL AFTER `min_number_of_repayments`;
ALTER TABLE `m_product_loan`
ALTER `min_principal_amount` DROP DEFAULT,
ALTER `max_principal_amount` DROP DEFAULT,
ALTER `min_nominal_interest_rate_per_period` DROP DEFAULT,
ALTER `max_nominal_interest_rate_per_period` DROP DEFAULT,
ALTER `min_number_of_repayments` DROP DEFAULT,
ALTER `max_number_of_repayments` DROP DEFAULT;
ALTER TABLE `m_product_loan`
CHANGE COLUMN `min_principal_amount` `min_principal_amount` DECIMAL(19,6) NULL AFTER `principal_amount`,
CHANGE COLUMN `max_principal_amount` `max_principal_amount` DECIMAL(19,6) NULL AFTER `min_principal_amount`,
CHANGE COLUMN `min_nominal_interest_rate_per_period` `min_nominal_interest_rate_per_period` DECIMAL(19,6) NULL AFTER `nominal_interest_rate_per_period`,
CHANGE COLUMN `max_nominal_interest_rate_per_period` `max_nominal_interest_rate_per_period` DECIMAL(19,6) NULL AFTER `min_nominal_interest_rate_per_period`,
CHANGE COLUMN `min_number_of_repayments` `min_number_of_repayments` SMALLINT(5) NULL AFTER `number_of_repayments`,
CHANGE COLUMN `max_number_of_repayments` `max_number_of_repayments` SMALLINT(5) NULL AFTER `min_number_of_repayments`;
@@ -289,6 +289,19 @@ public final class JsonCommand {
}
return isChanged;
}
public boolean isChangeInBigDecimalParameterNamedWithNullCheck(final String parameterName, final BigDecimal existingValue) {
boolean isChanged = false;
if (parameterExists(parameterName)) {
final BigDecimal workingValue = bigDecimalValueOfParameterNamed(parameterName);
if(workingValue == null && existingValue != null){
isChanged = true;
}else{
isChanged = differenceExists(existingValue, workingValue);
}
}
return isChanged;
}
public BigDecimal bigDecimalValueOfParameterNamed(final String parameterName) {
return this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(parameterName, parsedCommand);
@@ -303,6 +316,19 @@ public final class JsonCommand {
return isChanged;
}
public boolean isChangeInIntegerParameterNamedWithNullCheck(final String parameterName, final Integer existingValue) {
boolean isChanged = false;
if (parameterExists(parameterName)) {
final Integer workingValue = integerValueOfParameterNamed(parameterName);
if(workingValue == null && existingValue != null){
isChanged = true;
}else{
isChanged = differenceExists(existingValue, workingValue);
}
}
return isChanged;
}
public Integer integerValueOfParameterNamed(final String parameterName) {
return this.fromApiJsonHelper.extractIntegerWithLocaleNamed(parameterName, parsedCommand);
}
@@ -388,6 +388,40 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder notLessThanMin(final BigDecimal min) {
if (min != null && value != null){
BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString()));
if (amount.compareTo(min) == -1) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.less.than.min");
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter) .append(" value ").append(amount)
.append(" must not be less than minimum value ").append(min);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, amount, min);
dataValidationErrors.add(error);
return this;
}
}
return this;
}
public DataValidatorBuilder notGreaterThanMax(final BigDecimal max) {
if (max != null && value != null){
BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString()));
if (amount.compareTo(max) == 1) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.greater.than.max");
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter) .append(" value ").append(amount)
.append(" must not be more than maximum value ").append(max);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, amount, max);
dataValidationErrors.add(error);
return this;
}
}
return this;
}
public DataValidatorBuilder comapareMinAndMaxOfTwoBigDecmimalNos(final BigDecimal min, final BigDecimal max) {
if (min != null && max != null)
@@ -423,4 +457,40 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder notLessThanMin(final Integer min) {
if (value == null && ignoreNullValue) { return this; }
if (value != null && min != null) {
Integer number = Integer.valueOf(value.toString());
if (number < min) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.less.than.min");
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter)
.append(" must be greater than minimum value ").append(min);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, number, min);
dataValidationErrors.add(error);
}
}
return this;
}
public DataValidatorBuilder notGreaterThanMax(final Integer max) {
if (value == null && ignoreNullValue) { return this; }
if (value != null && max != null) {
Integer number = Integer.valueOf(value.toString());
if (number > max) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.greater.than.max");
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter)
.append(" must be less than maximum value ").append(max);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, number, max);
dataValidationErrors.add(error);
}
}
return this;
}
}
@@ -1321,6 +1321,8 @@ public class Loan extends AbstractPersistable<Long> {
final BigDecimal maxPrincipal = this.loanRepaymentScheduleDetail.getMaxPrincipal().getAmount();
final BigDecimal inArrearsTolerance = this.loanRepaymentScheduleDetail.getInArrearsTolerance().getAmount();
final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
final BigDecimal minInterestRatePerPeriod = this.loanRepaymentScheduleDetail.getMinNominalInterestRatePerPeriod();
final BigDecimal maxInterestRatePerPeriod = this.loanRepaymentScheduleDetail.getMaxNominalInterestRatePerPeriod();
final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
final BigDecimal defaultAnnualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
@@ -1329,13 +1331,16 @@ public class Loan extends AbstractPersistable<Long> {
final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
final Integer minNumberOfRepayments = this.loanRepaymentScheduleDetail.getMinNumberOfRepayments();
final Integer maxNumberOfRepayments = this.loanRepaymentScheduleDetail.getMaxNumberOfRepayments();
final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod();
final Integer loanTermFrequency = this.termFrequency;
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType);
final LoanSchedule loanSchedule = new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, minPrincipal,
maxPrincipal, interestRatePerPeriod, interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod,
interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod,
maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestRatePeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery,
repaymentPeriodFrequencyType, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, amortizationMethod,
loanTermFrequency, loanTermPeriodFrequencyType, setOfLoanCharges(), this.getDisbursementDate(),
this.getExpectedFirstRepaymentOnDate(), this.getInterestChargedFromDate(), inArrearsTolerance);
@@ -30,6 +30,8 @@ public final class LoanSchedule {
private final BigDecimal minPrincipal;
private final BigDecimal maxPrincipal;
private final BigDecimal nominalInterestRatePerPeriod;
private final BigDecimal minNominalInterestRatePerPeriod;
private final BigDecimal maxNominalInterestRatePerPeriod;
private final PeriodFrequencyType interestRatePeriodFrequencyType;
private final BigDecimal nominalAnnualInterestRate;
private final InterestMethod interestMethod;
@@ -37,6 +39,8 @@ public final class LoanSchedule {
private final Integer repaymentEvery;
private final PeriodFrequencyType repaymentPeriodFrequencyType;
private final Integer numberOfRepayments;
private final Integer minNumberOfRepayments;
private final Integer maxNumberOfRepayments;
private final AmortizationMethod amortizationMethod;
private final Integer loanTermFrequency;
private final PeriodFrequencyType loanTermPeriodFrequencyType;
@@ -49,10 +53,10 @@ public final class LoanSchedule {
public LoanSchedule(final LoanScheduleGenerator loanScheduleGenerator, final ApplicationCurrency applicationCurrency,
final BigDecimal principal, final BigDecimal minPrincipal, final BigDecimal maxPrincipal,
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
final BigDecimal nominalInterestRatePerPeriod, final BigDecimal minNominalInterestRatePerPeriod, final BigDecimal maxNominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repaymentEvery,
final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments,
final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments, final Integer minNumberOfRepayments, final Integer maxNumberOfRepayments,
final AmortizationMethod amortizationMethod, final Integer loanTermFrequency,
final PeriodFrequencyType loanTermPeriodFrequencyType, final Set<LoanCharge> loanCharges, final LocalDate disbursementDate,
final LocalDate repaymentStartFromDate, final LocalDate interestChargedFromDate, final BigDecimal inArrearsTolerance) {
@@ -62,6 +66,8 @@ public final class LoanSchedule {
this.minPrincipal = minPrincipal;
this.maxPrincipal = maxPrincipal;
this.nominalInterestRatePerPeriod = nominalInterestRatePerPeriod;
this.minNominalInterestRatePerPeriod = minNominalInterestRatePerPeriod;
this.maxNominalInterestRatePerPeriod = maxNominalInterestRatePerPeriod;
this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType;
this.nominalAnnualInterestRate = nominalAnnualInterestRate;
this.interestMethod = interestMethod;
@@ -69,6 +75,8 @@ public final class LoanSchedule {
this.repaymentEvery = repaymentEvery;
this.repaymentPeriodFrequencyType = repaymentPeriodFrequencyType;
this.numberOfRepayments = numberOfRepayments;
this.minNumberOfRepayments = minNumberOfRepayments;
this.maxNumberOfRepayments = maxNumberOfRepayments;
this.amortizationMethod = amortizationMethod;
this.loanTermFrequency = loanTermFrequency;
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
@@ -87,9 +95,9 @@ public final class LoanSchedule {
public LoanProductRelatedDetail loanProductRelatedDetail() {
final MonetaryCurrency currency = new MonetaryCurrency(applicationCurrency.getCode(), applicationCurrency.getDecimalPlaces());
return LoanProductRelatedDetail.createFrom(currency, principal, minPrincipal, maxPrincipal, nominalInterestRatePerPeriod,
return LoanProductRelatedDetail.createFrom(currency, principal, minPrincipal, maxPrincipal, nominalInterestRatePerPeriod, minNominalInterestRatePerPeriod, maxNominalInterestRatePerPeriod,
interestRatePeriodFrequencyType, nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, this.inArrearsTolerance);
repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, amortizationMethod, this.inArrearsTolerance);
}
public Integer getLoanTermFrequency() {
@@ -75,6 +75,8 @@ public class LoanScheduleAssembler {
final BigDecimal minPrincipal = loanProduct.getMinPrincipalAmount().getAmount();//Copy minPrincipal from loan product to loan
final BigDecimal maxPrincipal = loanProduct.getMaxPrincipalAmount().getAmount();//Copy maxPrincipal from loan product to loan
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
final BigDecimal minInterestRatePerPeriod = loanProduct.getMinNominalInterestRatePerPeriod();//Copy from Loan product to Loan
final BigDecimal maxInterestRatePerPeriod = loanProduct.getMaxNominalInterestRatePerPeriod();//Copy from Loan product to Loan
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestRateFrequencyType", element);
final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType",
@@ -91,6 +93,8 @@ public class LoanScheduleAssembler {
final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
final Integer repaymentFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
final Integer minNumberOfRepayments = loanProduct.getMinNumberOfRepayments();//Copy from Loan product to Loan
final Integer maxNumberOfRepayments = loanProduct.getMaxNumberOfRepayments();//Copy from Loan product to Loan
final Integer amortizationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
final PeriodFrequencyType repaymentPeriodFrequencyType = PeriodFrequencyType.fromInt(repaymentFrequencyType);
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);
@@ -107,9 +111,9 @@ public class LoanScheduleAssembler {
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod);
return new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod,
return new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod,
interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, loanTermFrequency,
repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, amortizationMethod, loanTermFrequency,
loanTermPeriodFrequencyType, loanCharges, expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate, inArrearsTolerance);
}
}
@@ -57,6 +57,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.useradministration.domain.AppUser;
@@ -92,19 +93,21 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
private final CalendarRepository calendarRepository;
private final CalendarInstanceRepository calendarInstanceRepository;
private final LoanProductCommandFromApiJsonDeserializer loanProductCommandFromApiJsonDeserializer;
@Autowired
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
final LoanApplicationTransitionApiJsonValidator loanApplicationTransitionApiJsonValidator,
final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer, final AprCalculator aprCalculator,
final LoanAssembler loanAssembler, final LoanChargeAssembler loanChargeAssembler,
final CollateralAssembler loanCollateralAssembler, final LoanRepository loanRepository,
final NoteRepository noteRepository, final LoanScheduleCalculationPlatformService calculationPlatformService,
final ClientRepository clientRepository, final LoanProductRepository loanProductRepository,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final LoanSummaryWrapper loanSummaryWrapper,
final CollateralAssembler loanCollateralAssembler, final LoanRepository loanRepository, final NoteRepository noteRepository,
final LoanScheduleCalculationPlatformService calculationPlatformService, final ClientRepository clientRepository,
final LoanProductRepository loanProductRepository, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory,
final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository) {
this.context = context;
final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository,
final LoanProductCommandFromApiJsonDeserializer loanProductCommandFromApiJsonDeserializer) {
this.context = context;
this.fromJsonHelper = fromJsonHelper;
this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
@@ -122,6 +125,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
this.calendarRepository = calendarRepository;
this.calendarInstanceRepository = calendarInstanceRepository;
this.loanProductCommandFromApiJsonDeserializer = loanProductCommandFromApiJsonDeserializer;
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -199,6 +203,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
final Loan existingLoanApplication = retrieveLoanBy(loanId);
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), existingLoanApplication.repaymentScheduleDetail(), "loan");
if (!existingLoanApplication.isSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(
loanId); }
@@ -42,11 +42,11 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingS
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -69,7 +69,7 @@ public class LoanAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final LoanSummaryWrapper loanSummaryWrapper;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
private final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer;
private final LoanProductCommandFromApiJsonDeserializer loanProductCommandFromApiJsonDeserializer;
@Autowired
public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
@@ -79,7 +79,7 @@ public class LoanAssembler {
final LoanScheduleAssembler loanScheduleAssembler, final LoanChargeAssembler loanChargeAssembler,
final CollateralAssembler loanCollateralAssembler, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer) {
final LoanProductCommandFromApiJsonDeserializer loanProductCommandFromApiJsonDeserializer) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository;
@@ -93,7 +93,7 @@ public class LoanAssembler {
this.loanCollateralAssembler = loanCollateralAssembler;
this.loanSummaryWrapper = loanSummaryWrapper;
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.loanProductCommandFromApiJsonDeserializer = loanProductCommandFromApiJsonDeserializer;
}
public Loan assembleFrom(final JsonCommand command, final AppUser currentUser) {
@@ -118,7 +118,7 @@ public class LoanAssembler {
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
//validate min and max constraint parameters
this.fromApiJsonDeserializer.validateMinMaxConstraintValues(element, loanProduct);
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(element, loanProduct.loanProductRelatedDetail(), "loan");
final Fund fund = findFundByIdIfProvided(fundId);
final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId);
@@ -57,11 +57,12 @@ import org.springframework.stereotype.Component;
public class LoanProductsApiResource {
private final Set<String> LOAN_PRODUCT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "description", "fundId",
"fundName", "currency", "principal", "numberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "interestRatePerPeriod",
"interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType", "interestCalculationPeriodType",
"inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "charges", "accountingRule",
"accountingMappings", "fundOptions", "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions",
"amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"fundName", "currency", "principal", "minPrincipal", "maxPrincipal", "numberOfRepayments", "minNumberOfRepayments",
"maxNumberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod",
"maxInterestRatePerPeriod", "interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType",
"interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
"charges", "accountingRule", "accountingMappings", "fundOptions", "currencyOptions", "repaymentFrequencyTypeOptions",
"interestRateFrequencyTypeOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions",
"accountingMappingOptions"));
@@ -40,9 +40,13 @@ public class LoanProductData {
private BigDecimal minPrincipal;
private BigDecimal maxPrincipal;
private final Integer numberOfRepayments;
private final Integer minNumberOfRepayments;
private final Integer maxNumberOfRepayments;
private final Integer repaymentEvery;
private final EnumOptionData repaymentFrequencyType;
private final BigDecimal interestRatePerPeriod;
private final BigDecimal minInterestRatePerPeriod;
private final BigDecimal maxInterestRatePerPeriod;
private final EnumOptionData interestRateFrequencyType;
private final BigDecimal annualInterestRate;
@@ -86,8 +90,12 @@ public class LoanProductData {
final BigDecimal maxPrincipal = null;
final BigDecimal tolerance = null;
final Integer numberOfRepayments = null;
final Integer minNumberOfRepayments = null;
final Integer maxNumberOfRepayments = null;
final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null;
final BigDecimal minInterestRatePerPeriod = null;
final BigDecimal maxInterestRatePerPeriod = null;
final BigDecimal annualInterestRate = null;
final EnumOptionData repaymentFrequencyType = null;
final EnumOptionData interestRateFrequencyType = null;
@@ -101,8 +109,8 @@ public class LoanProductData {
final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = null;
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery,
interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyName, charges, accountingType);
}
@@ -114,8 +122,12 @@ public class LoanProductData {
final BigDecimal maxPrincipal = null;
final BigDecimal tolerance = null;
final Integer numberOfRepayments = null;
final Integer minNumberOfRepayments = null;
final Integer maxNumberOfRepayments = null;
final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null;
final BigDecimal minInterestRatePerPeriod = null;
final BigDecimal maxInterestRatePerPeriod = null;
final BigDecimal annualInterestRate = null;
final EnumOptionData repaymentFrequencyType = null;
final EnumOptionData interestRateFrequencyType = null;
@@ -129,8 +141,8 @@ public class LoanProductData {
final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = null;
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery,
interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyName, charges, accountingType);
}
@@ -145,8 +157,12 @@ public class LoanProductData {
final BigDecimal maxPrincipal = null;
final BigDecimal tolerance = null;
final Integer numberOfRepayments = null;
final Integer minNumberOfRepayments = null;
final Integer maxNumberOfRepayments = null;
final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null;
final BigDecimal minInterestRatePerPeriod = null;
final BigDecimal maxInterestRatePerPeriod = null;
final BigDecimal annualInterestRate = null;
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(PeriodFrequencyType.MONTHS);
@@ -161,8 +177,9 @@ public class LoanProductData {
final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = LoanEnumerations.accountingRuleType(AccountingRuleType.NONE);
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments,
minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod,
maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyName, charges, accountingType);
}
@@ -178,12 +195,14 @@ public class LoanProductData {
public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency,
final BigDecimal principal, final BigDecimal minPrincipal, final BigDecimal maxPrincipal, final BigDecimal tolerance,
final Integer numberOfRepayments, final Integer repaymentEvery, final BigDecimal interestRatePerPeriod,
final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType,
final Integer numberOfRepayments, final Integer minNumberOfRepayments, final Integer maxNumberOfRepayments,
final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final BigDecimal minInterestRatePerPeriod,
final BigDecimal maxInterestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType,
final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName,
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
final Collection<ChargeData> charges, final EnumOptionData accountingType) {
this.id = id;
this.name = name;
this.description = description;
@@ -193,8 +212,12 @@ public class LoanProductData {
this.maxPrincipal = maxPrincipal;
this.inArrearsTolerance = tolerance;
this.numberOfRepayments = numberOfRepayments;
this.minNumberOfRepayments = minNumberOfRepayments;
this.maxNumberOfRepayments = maxNumberOfRepayments;
this.repaymentEvery = repaymentEvery;
this.interestRatePerPeriod = interestRatePerPeriod;
this.minInterestRatePerPeriod = minInterestRatePerPeriod;
this.maxInterestRatePerPeriod = maxInterestRatePerPeriod;
this.annualInterestRate = annualInterestRate;
this.repaymentFrequencyType = repaymentFrequencyType;
this.interestRateFrequencyType = interestRateFrequencyType;
@@ -227,8 +250,7 @@ public class LoanProductData {
final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> amortizationTypeOptions,
final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions,
final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions,
final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
final Collection<FundData> fundOptions, final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
final Map<String, Object> accountingMappings) {
this.id = productData.id;
@@ -242,8 +264,12 @@ public class LoanProductData {
this.maxPrincipal = productData.maxPrincipal;
this.inArrearsTolerance = productData.inArrearsTolerance;
this.numberOfRepayments = productData.numberOfRepayments;
this.minNumberOfRepayments = productData.minNumberOfRepayments;
this.maxNumberOfRepayments = productData.maxNumberOfRepayments;
this.repaymentEvery = productData.repaymentEvery;
this.interestRatePerPeriod = productData.interestRatePerPeriod;
this.minInterestRatePerPeriod = productData.minInterestRatePerPeriod;
this.maxInterestRatePerPeriod = productData.maxInterestRatePerPeriod;
this.annualInterestRate = productData.annualInterestRate;
this.repaymentFrequencyType = productData.repaymentFrequencyType;
this.interestRateFrequencyType = productData.interestRateFrequencyType;
@@ -91,16 +91,20 @@ public class LoanProduct extends AbstractPersistable<Long> {
final PeriodFrequencyType interestFrequencyType = PeriodFrequencyType.fromInt(command
.integerValueOfParameterNamed("interestRateFrequencyType"));
final BigDecimal interestRatePerPeriod = command.bigDecimalValueOfParameterNamed("interestRatePerPeriod");
final BigDecimal minInterestRatePerPeriod = command.bigDecimalValueOfParameterNamed("minInterestRatePerPeriod");
final BigDecimal maxInterestRatePerPeriod = command.bigDecimalValueOfParameterNamed("maxInterestRatePerPeriod");
final BigDecimal annualInterestRate = aprCalculator.calculateFrom(interestFrequencyType, interestRatePerPeriod);
final Integer repaymentEvery = command.integerValueOfParameterNamed("repaymentEvery");
final Integer numberOfRepayments = command.integerValueOfParameterNamed("numberOfRepayments");
final Integer minNumberOfRepayments = command.integerValueOfParameterNamed("minNumberOfRepayments");
final Integer maxNumberOfRepayments = command.integerValueOfParameterNamed("maxNumberOfRepayments");
final BigDecimal inArrearsTolerance = command.bigDecimalValueOfParameterNamed("inArrearsTolerance");
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal,
interestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationMethod, inArrearsTolerance, productCharges,
interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequencyType, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, amortizationMethod, inArrearsTolerance, productCharges,
accountingRuleType);
}
@@ -111,10 +115,12 @@ public class LoanProduct extends AbstractPersistable<Long> {
public LoanProduct(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final String name,
final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
final BigDecimal defaultMinPrincipal, final BigDecimal defaultMaxPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultNominalInterestRatePerPeriod, final BigDecimal defaultMinNominalInterestRatePerPeriod,
final BigDecimal defaultMaxNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments,
final Integer defaultMinNumberOfInstallments, final Integer defaultMaxNumberOfInstallments,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges,
final AccountingRuleType accountingRuleType) {
this.fund = fund;
@@ -131,9 +137,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
}
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal,
defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod,
interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod,
inArrearsTolerance);
defaultNominalInterestRatePerPeriod, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
repaymentFrequencyType, defaultNumberOfInstallments, defaultMinNumberOfInstallments, defaultMaxNumberOfInstallments,
amortizationMethod, inArrearsTolerance);
if (accountingRuleType != null) {
this.accountingRule = accountingRuleType.getValue();
@@ -234,6 +241,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
return AccountingRuleType.ACCRUAL_BASED.getValue().equals(this.accountingRule);
}
public Money getPrincipalAmount(){
return this.loanProductRelatedDetail.getPrincipal();
}
public Money getMinPrincipalAmount(){
return this.loanProductRelatedDetail.getMinPrincipal();
}
@@ -241,4 +252,32 @@ public class LoanProduct extends AbstractPersistable<Long> {
public Money getMaxPrincipalAmount(){
return this.loanProductRelatedDetail.getMaxPrincipal();
}
public BigDecimal getNominalInterestRatePerPeriod() {
return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod();
}
public BigDecimal getMinNominalInterestRatePerPeriod() {
return this.loanProductRelatedDetail.getMinNominalInterestRatePerPeriod();
}
public BigDecimal getMaxNominalInterestRatePerPeriod(){
return this.loanProductRelatedDetail.getMaxNominalInterestRatePerPeriod();
}
public Integer getNumberOfRepayments(){
return this.loanProductRelatedDetail.getNumberOfRepayments();
}
public Integer getMinNumberOfRepayments() {
return this.loanProductRelatedDetail.getMinNumberOfRepayments();
}
public Integer getMaxNumberOfRepayments() {
return this.loanProductRelatedDetail.getMaxNumberOfRepayments();
}
public LoanProductRelatedDetail loanProductRelatedDetail(){
return this.loanProductRelatedDetail;
}
}
@@ -20,7 +20,6 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanproduct.exception.NotInMinMaxRangeException;
/**
* LoanRepaymentScheduleDetail encapsulates all the details of a
@@ -35,14 +34,20 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = false)
private BigDecimal principal;
@Column(name = "min_principal_amount", scale = 6, precision = 19, nullable = false)
@Column(name = "min_principal_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal minPrincipal;
@Column(name = "max_principal_amount", scale = 6, precision = 19, nullable = false)
@Column(name = "max_principal_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal maxPrincipal;
@Column(name = "nominal_interest_rate_per_period", scale = 6, precision = 19, nullable = false)
private BigDecimal nominalInterestRatePerPeriod;
@Column(name = "min_nominal_interest_rate_per_period", scale = 6, precision = 19, nullable = true)
private BigDecimal minNominalInterestRatePerPeriod;
@Column(name = "max_nominal_interest_rate_per_period", scale = 6, precision = 19, nullable = true)
private BigDecimal maxNominalInterestRatePerPeriod;
// FIXME - move away form JPA ordinal use for enums using just integer -
// requires sql patch for existing users of software.
@@ -77,6 +82,12 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "number_of_repayments", nullable = false)
private Integer numberOfRepayments;
@Column(name = "min_number_of_repayments", nullable = true)
private Integer minNumberOfRepayments;
@Column(name = "max_number_of_repayments", nullable = true)
private Integer maxNumberOfRepayments;
// FIXME - move away form JPA ordinal use for enums using just integer -
// requires sql patch for existing users of software.
@Enumerated(EnumType.ORDINAL)
@@ -88,14 +99,17 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal,
final BigDecimal minPrincipal, final BigDecimal maxPrincipal, final BigDecimal nominalInterestRatePerPeriod,
final BigDecimal minNominalInterestRatePerPeriod, final BigDecimal maxNominalInterestRatePerPeriod,
final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalAnnualInterestRate,
final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance) {
final Integer minNumberOfRepayments, final Integer maxNumberOfRepayments, final AmortizationMethod amortizationMethod,
final BigDecimal inArrearsTolerance) {
return new LoanProductRelatedDetail(currency, principal, minPrincipal, maxPrincipal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
return new LoanProductRelatedDetail(currency, principal, minPrincipal, maxPrincipal, nominalInterestRatePerPeriod,
minNominalInterestRatePerPeriod, maxNominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType,
numberOfRepayments, amortizationMethod, inArrearsTolerance);
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, amortizationMethod, inArrearsTolerance);
}
protected LoanProductRelatedDetail() {
@@ -104,16 +118,20 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
public LoanProductRelatedDetail(final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
final BigDecimal defaultMinPrincipal, final BigDecimal defaultMaxPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultNominalInterestRatePerPeriod, final BigDecimal defaultMinNominalInterestRatePerPeriod,
final BigDecimal defaultMaxNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfRepayments,
final Integer defaultMinNumberOfRepayments, final Integer defaultMaxNumberOfRepayments,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance) {
this.currency = currency;
this.principal = defaultPrincipal;
this.minPrincipal = defaultMinPrincipal;
this.maxPrincipal = defaultMaxPrincipal;
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
this.minNominalInterestRatePerPeriod = defaultMinNominalInterestRatePerPeriod;
this.maxNominalInterestRatePerPeriod = defaultMaxNominalInterestRatePerPeriod;
this.interestPeriodFrequencyType = interestPeriodFrequencyType;
this.annualNominalInterestRate = defaultAnnualNominalInterestRate;
this.interestMethod = interestMethod;
@@ -121,6 +139,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.repayEvery = repayEvery;
this.repaymentPeriodFrequencyType = repaymentFrequencyType;
this.numberOfRepayments = defaultNumberOfRepayments;
this.minNumberOfRepayments = defaultMinNumberOfRepayments;
this.maxNumberOfRepayments = defaultMaxNumberOfRepayments;
this.amortizationMethod = amortizationMethod;
if (inArrearsTolerance != null && BigDecimal.ZERO.compareTo(inArrearsTolerance) == 0) {
this.inArrearsTolerance = null;
@@ -152,7 +172,17 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
public BigDecimal getNominalInterestRatePerPeriod() {
return BigDecimal.valueOf(Double.valueOf(this.nominalInterestRatePerPeriod.stripTrailingZeros().toString()));
}
public BigDecimal getMinNominalInterestRatePerPeriod() {
return this.minNominalInterestRatePerPeriod == null ? null : BigDecimal.valueOf(Double.valueOf(this.minNominalInterestRatePerPeriod
.stripTrailingZeros().toString()));
}
public BigDecimal getMaxNominalInterestRatePerPeriod() {
return this.maxNominalInterestRatePerPeriod == null ? null : BigDecimal.valueOf(Double.valueOf(this.maxNominalInterestRatePerPeriod
.stripTrailingZeros().toString()));
}
public PeriodFrequencyType getInterestPeriodFrequencyType() {
return interestPeriodFrequencyType;
}
@@ -183,7 +213,15 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
public Integer getNumberOfRepayments() {
return numberOfRepayments;
}
public Integer getMinNumberOfRepayments() {
return minNumberOfRepayments;
}
public Integer getMaxNumberOfRepayments() {
return maxNumberOfRepayments;
}
public AmortizationMethod getAmortizationMethod() {
return amortizationMethod;
}
@@ -227,7 +265,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
final String localeAsInput = command.locale();
final String minPrincipalParamName = "minPrincipal";
if (command.isChangeInBigDecimalParameterNamed(minPrincipalParamName, this.minPrincipal)) {
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(minPrincipalParamName, this.minPrincipal)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minPrincipalParamName);
actualChanges.put(minPrincipalParamName, newValue);
actualChanges.put("locale", localeAsInput);
@@ -235,7 +273,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
}
final String maxPrincipalParamName = "maxPrincipal";
if (command.isChangeInBigDecimalParameterNamed(maxPrincipalParamName, this.maxPrincipal)) {
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(maxPrincipalParamName, this.maxPrincipal)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(maxPrincipalParamName);
actualChanges.put(maxPrincipalParamName, newValue);
actualChanges.put("locale", localeAsInput);
@@ -248,7 +286,6 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
actualChanges.put(principalParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.principal = newValue;
validatePrincipalAmount();
}
final String repaymentEveryParamName = "repaymentEvery";
@@ -267,6 +304,22 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.repaymentPeriodFrequencyType = PeriodFrequencyType.fromInt(newValue);
}
final String minNumberOfRepaymentsParamName = "minNumberOfRepayments";
if (command.isChangeInIntegerParameterNamed(minNumberOfRepaymentsParamName, this.minNumberOfRepayments)) {
final Integer newValue = command.integerValueOfParameterNamed(minNumberOfRepaymentsParamName);
actualChanges.put(minNumberOfRepaymentsParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.minNumberOfRepayments = newValue;
}
final String maxNumberOfRepaymentsParamName = "maxNumberOfRepayments";
if (command.isChangeInIntegerParameterNamed(maxNumberOfRepaymentsParamName, this.maxNumberOfRepayments)) {
final Integer newValue = command.integerValueOfParameterNamed(maxNumberOfRepaymentsParamName);
actualChanges.put(maxNumberOfRepaymentsParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.maxNumberOfRepayments = newValue;
}
final String numberOfRepaymentsParamName = "numberOfRepayments";
if (command.isChangeInIntegerParameterNamed(numberOfRepaymentsParamName, this.numberOfRepayments)) {
final Integer newValue = command.integerValueOfParameterNamed(numberOfRepaymentsParamName);
@@ -291,6 +344,22 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.inArrearsTolerance = newValue;
}
final String minInterestRatePerPeriodParamName = "minInterestRatePerPeriod";
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(minInterestRatePerPeriodParamName, this.minNominalInterestRatePerPeriod)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minInterestRatePerPeriodParamName);
actualChanges.put(minInterestRatePerPeriodParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.minNominalInterestRatePerPeriod = newValue;
}
final String maxInterestRatePerPeriodParamName = "maxInterestRatePerPeriod";
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(maxInterestRatePerPeriodParamName, this.maxNominalInterestRatePerPeriod)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(maxInterestRatePerPeriodParamName);
actualChanges.put(maxInterestRatePerPeriodParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.maxNominalInterestRatePerPeriod = newValue;
}
final String interestRatePerPeriodParamName = "interestRatePerPeriod";
if (command.isChangeInBigDecimalParameterNamed(interestRatePerPeriodParamName, this.nominalInterestRatePerPeriod)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(interestRatePerPeriodParamName);
@@ -329,13 +398,6 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
return actualChanges;
}
private void validatePrincipalAmount() {
if(this.principal.compareTo(this.minPrincipal) == -1 || this.principal.compareTo(maxPrincipal) == 1){
final String errorMessage = "The Principal amount " + this.principal + " is invalid. Must be an amount between " + this.minPrincipal + " and " + this.maxPrincipal + " inclusive.";
throw new NotInMinMaxRangeException("loan", "principal.amount", errorMessage, this.principal, this.minPrincipal, this.maxPrincipal);
}
}
private void updateInterestRateDerivedFields(final AprCalculator aprCalculator) {
this.annualNominalInterestRate = aprCalculator.calculateFrom(this.interestPeriodFrequencyType, this.nominalInterestRatePerPeriod);
}
@@ -23,6 +23,7 @@ import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -36,9 +37,10 @@ public final class LoanProductCommandFromApiJsonDeserializer {
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("locale", "name", "description", "fundId",
"currencyCode", "digitsAfterDecimal", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType",
"interestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"inArrearsTolerance", "transactionProcessingStrategyId", "charges", "accountingRule",
"currencyCode", "digitsAfterDecimal", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments",
"minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod",
"minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
"interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "charges", "accountingRule",
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
@@ -80,29 +82,104 @@ public final class LoanProductCommandFromApiJsonDeserializer {
final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault());
baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
final BigDecimal minPrincipal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("minPrincipal", element);
baseDataValidator.reset().parameter("minPrincipal").value(minPrincipal).notNull().positiveAmount();
final BigDecimal maxPrincipal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("maxPrincipal", element);
baseDataValidator.reset().parameter("maxPrincipal").value(maxPrincipal).notNull().positiveAmount();
final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount().inMinAndMaxAmountRange(minPrincipal, maxPrincipal);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();
String minPrincipalParameterName = "minPrincipal";
BigDecimal minPrincipalAmount = null;
if(fromApiJsonHelper.parameterExists(minPrincipalParameterName, element)){
minPrincipalAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minPrincipalParameterName, element);
baseDataValidator.reset().parameter(minPrincipalParameterName).value(minPrincipalAmount).ignoreIfNull().positiveAmount();
}
String maxPrincipalParameterName = "maxPrincipal";
BigDecimal maxPrincipalAmount = null;
if(fromApiJsonHelper.parameterExists(maxPrincipalParameterName, element)){
maxPrincipalAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxPrincipalParameterName, element);
baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).ignoreIfNull().positiveAmount();
}
if (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) != -1) {
if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) != -1) {
baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).notLessThanMin(minPrincipalAmount);
if(minPrincipalAmount.compareTo(maxPrincipalAmount) <= 0){
baseDataValidator.reset().parameter("principal").value(principal).inMinAndMaxAmountRange(minPrincipalAmount, maxPrincipalAmount);
}
}else{
baseDataValidator.reset().parameter("principal").value(principal).notGreaterThanMax(maxPrincipalAmount);
}
}else if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) != -1) {
baseDataValidator.reset().parameter("principal").value(principal).notLessThanMin(minPrincipalAmount);
}
final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero();
String minNumberOfRepaymentsParameterName = "minNumberOfRepayments";
Integer minNumberOfRepayments = null;
if(fromApiJsonHelper.parameterExists(minNumberOfRepaymentsParameterName, element)){
minNumberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed(minNumberOfRepaymentsParameterName, element);
baseDataValidator.reset().parameter(minNumberOfRepaymentsParameterName).value(minNumberOfRepayments).ignoreIfNull().integerGreaterThanZero();
}
String maxNumberOfRepaymentsParameterName = "maxNumberOfRepayments";
Integer maxNumberOfRepayments = null;
if(fromApiJsonHelper.parameterExists(maxNumberOfRepaymentsParameterName, element)){
maxNumberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed(maxNumberOfRepaymentsParameterName, element);
baseDataValidator.reset().parameter(maxNumberOfRepaymentsParameterName).value(maxNumberOfRepayments).ignoreIfNull().integerGreaterThanZero();
}
if(maxNumberOfRepayments != null && maxNumberOfRepayments.compareTo(0) == 1){
if(minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) == 1){
baseDataValidator.reset().parameter(maxNumberOfRepaymentsParameterName).value(maxNumberOfRepayments).notLessThanMin(minNumberOfRepayments);
if(minNumberOfRepayments.compareTo(maxNumberOfRepayments) <= 0){
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).inMinMaxRange(minNumberOfRepayments, maxNumberOfRepayments);
}
}else{
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notGreaterThanMax(maxNumberOfRepayments);
}
}else if(minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) == 1){
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notLessThanMin(minNumberOfRepayments);
}
final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
baseDataValidator.reset().parameter("repaymentEvery").value(repaymentEvery).notNull().integerGreaterThanZero();
final Integer repaymentFrequencyType = fromApiJsonHelper
.extractIntegerNamed("repaymentFrequencyType", element, Locale.getDefault());
baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
BigDecimal minInterestRatePerPeriod = null;
if(fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)){
minInterestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName, element);
baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull().zeroOrPositiveAmount();
}
String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod";
BigDecimal maxInterestRatePerPeriod = null;
if(fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)){
maxInterestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName, element);
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull().zeroOrPositiveAmount();
}
if(maxInterestRatePerPeriod != null && maxInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1){
if(minInterestRatePerPeriod != null && minInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1){
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).notLessThanMin(minInterestRatePerPeriod);
if(minInterestRatePerPeriod.compareTo(maxInterestRatePerPeriod) <= 0){
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).inMinAndMaxAmountRange(minInterestRatePerPeriod, maxInterestRatePerPeriod);
}
}else{
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notGreaterThanMax(maxInterestRatePerPeriod);
}
}else if(minInterestRatePerPeriod != null && minInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1){
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notLessThanMin(minInterestRatePerPeriod);
}
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
@@ -223,28 +300,19 @@ public final class LoanProductCommandFromApiJsonDeserializer {
BigDecimal minPrincipalAmount = null;
if(fromApiJsonHelper.parameterExists(minPrincipalParameterName, element)){
minPrincipalAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minPrincipalParameterName, element);
baseDataValidator.reset().parameter(minPrincipalParameterName).value(minPrincipalAmount).ignoreIfNull().positiveAmount();
}
String maxPrincipalParameterName = "maxPrincipal";
BigDecimal maxPrincipalAmount = null;
if(fromApiJsonHelper.parameterExists(maxPrincipalParameterName, element)){
maxPrincipalAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxPrincipalParameterName, element);
}
//Principal amount is allowed to change only when Min and Max principal amounts are sent in JSON request
//Validate if minPrincipal amount or principal amount is part of JSON request.
if(fromApiJsonHelper.parameterExists(minPrincipalParameterName, element) || fromApiJsonHelper.parameterExists("principal", element)){
baseDataValidator.reset().parameter(minPrincipalParameterName).value(minPrincipalAmount).notNull().positiveAmount();
}
//Validate if maxPrincipal amount or principal amount is part of JSON request.
if(fromApiJsonHelper.parameterExists(maxPrincipalParameterName, element) || fromApiJsonHelper.parameterExists("principal", element)){
baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).notNull().positiveAmount();
baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).ignoreIfNull().positiveAmount();
}
if (fromApiJsonHelper.parameterExists("principal", element)) {
final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount().inMinAndMaxAmountRange(minPrincipalAmount, maxPrincipalAmount);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();
}
if (fromApiJsonHelper.parameterExists("inArrearsTolerance", element)) {
@@ -252,11 +320,39 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
}
if (fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
BigDecimal minInterestRatePerPeriod = null;
if (fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) {
minInterestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName, element);
baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull().zeroOrPositiveAmount();
}
String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod";
BigDecimal maxInterestRatePerPeriod = null;
if (fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) {
maxInterestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName, element);
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull().zeroOrPositiveAmount();
}
if (fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().positiveAmount();
}
String minNumberOfRepaymentsParameterName = "minNumberOfRepayments";
Integer minNumberOfRepayments = null;
if (fromApiJsonHelper.parameterExists(minNumberOfRepaymentsParameterName, element)) {
minNumberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed(minNumberOfRepaymentsParameterName, element);
baseDataValidator.reset().parameter(minNumberOfRepaymentsParameterName).value(minNumberOfRepayments).ignoreIfNull().integerGreaterThanZero();
}
String maxNumberOfRepaymentsParameterName = "maxNumberOfRepayments";
Integer maxNumberOfRepayments = null;
if (fromApiJsonHelper.parameterExists(maxNumberOfRepaymentsParameterName, element)) {
maxNumberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed(maxNumberOfRepaymentsParameterName, element);
baseDataValidator.reset().parameter(maxNumberOfRepaymentsParameterName).value(maxNumberOfRepayments).ignoreIfNull().integerGreaterThanZero();
}
if (fromApiJsonHelper.parameterExists("numberOfRepayments", element)) {
final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero();
@@ -352,6 +448,180 @@ public final class LoanProductCommandFromApiJsonDeserializer {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateMinMaxConstraints(final JsonElement element, final LoanProductRelatedDetail loanProductRelatedDetail, final String resource){
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(resource);
validatePrincipalMinMaxConstraint(element, loanProductRelatedDetail, baseDataValidator);
validateNumberOfRepaymentsMinMaxConstraint(element, loanProductRelatedDetail, baseDataValidator);
validateNominalInterestRatePerPeriodMinMaxConstraint(element, loanProductRelatedDetail, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void validatePrincipalMinMaxConstraint(final JsonElement element, final LoanProductRelatedDetail loanProductRelatedDetail, final DataValidatorBuilder baseDataValidator) {
boolean principalUpdated = false;
boolean minPrincipalUpdated = false;
boolean maxPrincipalUpdated = false;
String principalParameterName = "principal";
BigDecimal principalAmount = null;
if (fromApiJsonHelper.parameterExists(principalParameterName, element)) {
principalAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(principalParameterName, element);
principalUpdated = true;
}else{
principalAmount = loanProductRelatedDetail.getPrincipal().getAmount();
}
String minPrincipalParameterName = "minPrincipal";
BigDecimal minPrincipalAmount = null;
if(fromApiJsonHelper.parameterExists(minPrincipalParameterName, element)){
minPrincipalAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minPrincipalParameterName, element);
minPrincipalUpdated = true;
}else{
minPrincipalAmount = loanProductRelatedDetail.getMinPrincipal().getAmount();
}
String maxPrincipalParameterName = "maxPrincipal";
BigDecimal maxPrincipalAmount = null;
if(fromApiJsonHelper.parameterExists(maxPrincipalParameterName, element)){
maxPrincipalAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxPrincipalParameterName, element);
maxPrincipalUpdated = true;
}else{
maxPrincipalAmount = loanProductRelatedDetail.getMaxPrincipal().getAmount();
}
if(minPrincipalUpdated){
baseDataValidator.reset().parameter(minPrincipalParameterName).value(minPrincipalAmount).notGreaterThanMax(maxPrincipalAmount);
}
if(maxPrincipalUpdated){
baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).notLessThanMin(minPrincipalAmount);
}
if((principalUpdated || minPrincipalUpdated || maxPrincipalUpdated)){
if((minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) == 1) && (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) == 1)){
baseDataValidator.reset().parameter(principalParameterName).value(principalAmount).inMinAndMaxAmountRange(minPrincipalAmount, maxPrincipalAmount);
}else{
if(minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) == 1){
baseDataValidator.reset().parameter(principalParameterName).value(principalAmount).notLessThanMin(minPrincipalAmount);
}else if(maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) == 1){
baseDataValidator.reset().parameter(principalParameterName).value(principalAmount).notGreaterThanMax(maxPrincipalAmount);
}
}
}
}
private void validateNumberOfRepaymentsMinMaxConstraint(final JsonElement element, final LoanProductRelatedDetail loanProductRelatedDetail, final DataValidatorBuilder baseDataValidator) {
boolean numberOfRepaymentsUpdated = false;
boolean minNumberOfRepaymentsUpdated = false;
boolean maxNumberOfRepaymentsUpdated = false;
String numberOfRepaymentsParameterName = "numberOfRepayments";
Integer numberOfRepayments = null;
if (fromApiJsonHelper.parameterExists(numberOfRepaymentsParameterName, element)) {
numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed(numberOfRepaymentsParameterName, element);
numberOfRepaymentsUpdated = true;
}else{
numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
}
String minNumberOfRepaymentsParameterName = "minNumberOfRepayments";
Integer minNumberOfRepayments = null;
if (fromApiJsonHelper.parameterExists(minNumberOfRepaymentsParameterName, element)) {
minNumberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed(minNumberOfRepaymentsParameterName, element);
minNumberOfRepaymentsUpdated = true;
}else{
minNumberOfRepayments = loanProductRelatedDetail.getMinNumberOfRepayments();
}
String maxNumberOfRepaymentsParameterName = "maxNumberOfRepayments";
Integer maxNumberOfRepayments = null;
if (fromApiJsonHelper.parameterExists(maxNumberOfRepaymentsParameterName, element)) {
maxNumberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed(maxNumberOfRepaymentsParameterName, element);
maxNumberOfRepaymentsUpdated = true;
}else{
maxNumberOfRepayments = loanProductRelatedDetail.getMaxNumberOfRepayments();
}
if(minNumberOfRepaymentsUpdated){
baseDataValidator.reset().parameter(minNumberOfRepaymentsParameterName).value(minNumberOfRepayments).ignoreIfNull().notGreaterThanMax(maxNumberOfRepayments);
}
if(maxNumberOfRepaymentsUpdated){
baseDataValidator.reset().parameter(maxNumberOfRepaymentsParameterName).value(maxNumberOfRepayments).notLessThanMin(minNumberOfRepayments);
}
if(numberOfRepaymentsUpdated || minNumberOfRepaymentsUpdated || maxNumberOfRepaymentsUpdated){
if(maxNumberOfRepayments != null && maxNumberOfRepayments.compareTo(0) == 1){
if(minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) == 1){
baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments).inMinMaxRange(minNumberOfRepayments, maxNumberOfRepayments);
}else{
baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments).notGreaterThanMax(maxNumberOfRepayments);
}
}else if(minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) == 1){
baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments).notLessThanMin(minNumberOfRepayments);
}
}
}
private void validateNominalInterestRatePerPeriodMinMaxConstraint(final JsonElement element, final LoanProductRelatedDetail loanProductRelatedDetail, final DataValidatorBuilder baseDataValidator) {
boolean iRPUpdated = false;
boolean minIRPUpdated = false;
boolean maxIRPUpdated = false;
String interestRatePerPeriodParameterName = "interestRatePerPeriod";
BigDecimal interestRatePerPeriod = null;
if (fromApiJsonHelper.parameterExists(interestRatePerPeriodParameterName, element)) {
interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(interestRatePerPeriodParameterName, element);
iRPUpdated = true;
}else{
interestRatePerPeriod = loanProductRelatedDetail.getNominalInterestRatePerPeriod();
}
String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
BigDecimal minInterestRatePerPeriod = null;
if (fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) {
minInterestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName, element);
minIRPUpdated = true;
}else{
minInterestRatePerPeriod = loanProductRelatedDetail.getMinNominalInterestRatePerPeriod();
}
String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod";
BigDecimal maxInterestRatePerPeriod = null;
if (fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) {
maxInterestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName, element);
maxIRPUpdated = true;
}else{
maxInterestRatePerPeriod = loanProductRelatedDetail.getMaxNominalInterestRatePerPeriod();
}
if(minIRPUpdated){
baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull().notGreaterThanMax(maxInterestRatePerPeriod);
}
if(maxIRPUpdated){
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull().notLessThanMin(minInterestRatePerPeriod);
}
if(iRPUpdated || minIRPUpdated || maxIRPUpdated){
if(maxInterestRatePerPeriod != null){
if(minInterestRatePerPeriod != null){
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).inMinAndMaxAmountRange(minInterestRatePerPeriod, maxInterestRatePerPeriod);
}else{
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notGreaterThanMax(maxInterestRatePerPeriod);
}
}else if(minInterestRatePerPeriod != null){
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notLessThanMin(minInterestRatePerPeriod);
}
}
}
private boolean isCashBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType);
}
@@ -97,9 +97,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.loan_transaction_strategy_id as transactionStrategyId, ltps.name as transactionStrategyName, "
+ "lp.name as name, lp.description as description, "
+ "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, "
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,"
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, "
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, "
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
+ "lp.accounting_type as accountingType, "
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol "
@@ -135,12 +135,16 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final BigDecimal tolerance = rs.getBigDecimal("tolerance");
final Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments");
final Integer minNumberOfRepayments = JdbcSupport.getInteger(rs, "minNumberOfRepayments");
final Integer maxNumberOfRepayments = JdbcSupport.getInteger(rs, "maxNumberOfRepayments");
final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaidEvery");
final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = LoanEnumerations.accountingRuleType(accountingRuleId);
final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod");
final BigDecimal minInterestRatePerPeriod = rs.getBigDecimal("minInterestRatePerPeriod");
final BigDecimal maxInterestRatePerPeriod = rs.getBigDecimal("maxInterestRatePerPeriod");
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
final int repaymentFrequencyTypeId = JdbcSupport.getInteger(rs, "repaymentPeriodFrequency");
@@ -160,7 +164,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
.interestCalculationPeriodType(interestCalculationPeriodTypeId);
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, repaymentEvery, interestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
transactionStrategyId, transactionStrategyName, this.charges, accountingRuleType);
}
@@ -137,6 +137,8 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final LoanProduct product = this.loanProductRepository.findOne(loanProductId);
if (product == null) { throw new LoanProductNotFoundException(loanProductId); }
this.fromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), product.loanProductRelatedDetail(), "loanproduct");
final Map<String, Object> changes = product.update(command, this.aprCalculator);
if (changes.containsKey("fundId")) {
@@ -26,6 +26,8 @@ public class LoanProductRelatedDetailTestHelper {
// 2% per month, 24% per year
BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
BigDecimal defaultMinNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("1"));
BigDecimal defaultMaxNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("10"));
PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.MONTHS;
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.valueOf(Double.valueOf("24"));
@@ -35,13 +37,17 @@ public class LoanProductRelatedDetailTestHelper {
PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.MONTHS;
Integer defaultNumberOfRepayments = Integer.valueOf(4);
Integer defaultMinNumberOfRepayments = Integer.valueOf(2);
Integer defaultMaxNumberOfRepayments = Integer.valueOf(6);
AmortizationMethod amortizationMethod = AmortizationMethod.EQUAL_PRINCIPAL;
BigDecimal inArrearsTolerance = BigDecimal.ZERO;
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance);
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal,
defaultNominalInterestRatePerPeriod, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
repaymentFrequencyType, defaultNumberOfRepayments, defaultMinNumberOfRepayments, defaultMaxNumberOfRepayments,
amortizationMethod, inArrearsTolerance);
}
public static LoanProductRelatedDetail createSettingsForEqualInstallmentAmortizationQuarterly() {
@@ -52,6 +58,8 @@ public class LoanProductRelatedDetailTestHelper {
// 2% per month, 24% per year
BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
BigDecimal defaultMinNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
BigDecimal defaultMaxNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.MONTHS;
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.valueOf(Double.valueOf("24"));
@@ -61,13 +69,17 @@ public class LoanProductRelatedDetailTestHelper {
PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.MONTHS;
Integer defaultNumberOfRepayments = Integer.valueOf(4);
Integer defaultMinNumberOfRepayments = Integer.valueOf(4);
Integer defaultMaxNumberOfRepayments = Integer.valueOf(4);
AmortizationMethod amortizationMethod = AmortizationMethod.EQUAL_INSTALLMENTS;
BigDecimal inArrearsTolerance = BigDecimal.ZERO;
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance);
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal,
defaultNominalInterestRatePerPeriod, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
repaymentFrequencyType, defaultNumberOfRepayments, defaultMinNumberOfRepayments, defaultMaxNumberOfRepayments,
amortizationMethod, inArrearsTolerance);
}
public static LoanProductRelatedDetail createSettingsForFlatQuarterly(final AmortizationMethod amortizationMethod) {
@@ -79,6 +91,8 @@ public class LoanProductRelatedDetailTestHelper {
// 2% per month, 24% per year
BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
BigDecimal defaultMinNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
BigDecimal defaultMaxNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.MONTHS;
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.valueOf(Double.valueOf("24"));
@@ -88,12 +102,16 @@ public class LoanProductRelatedDetailTestHelper {
PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.MONTHS;
Integer defaultNumberOfRepayments = Integer.valueOf(4);
Integer defaultMinNumberOfRepayments = Integer.valueOf(4);
Integer defaultMaxNumberOfRepayments = Integer.valueOf(4);
BigDecimal inArrearsTolerance = BigDecimal.ZERO;
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance);
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal,
defaultNominalInterestRatePerPeriod, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
repaymentFrequencyType, defaultNumberOfRepayments, defaultMinNumberOfRepayments, defaultMaxNumberOfRepayments,
amortizationMethod, inArrearsTolerance);
}
public static LoanProductRelatedDetail createSettingsForIrregularFlatEveryFourMonths() {
@@ -105,6 +123,8 @@ public class LoanProductRelatedDetailTestHelper {
// 2% per month, 24% per year
BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
BigDecimal defaultMinNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2"));
BigDecimal defaultMaxNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("4"));
PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.MONTHS;
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.valueOf(Double.valueOf("24"));
@@ -114,26 +134,31 @@ public class LoanProductRelatedDetailTestHelper {
PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.MONTHS;
Integer defaultNumberOfRepayments = Integer.valueOf(2);
Integer defaultMinNumberOfRepayments = Integer.valueOf(2);
Integer defaultMaxNumberOfRepayments = Integer.valueOf(2);
BigDecimal inArrearsTolerance = BigDecimal.ZERO;
AmortizationMethod amortizationMethod = AmortizationMethod.EQUAL_PRINCIPAL;
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal,
defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod,
interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfRepayments, amortizationMethod,
defaultNominalInterestRatePerPeriod, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod,
interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfRepayments, defaultMinNumberOfRepayments, defaultMaxNumberOfRepayments, amortizationMethod,
inArrearsTolerance);
}
private static LoanProductRelatedDetail createLoanProductRelatedDetail(MonetaryCurrency currency, BigDecimal defaultPrincipal,
BigDecimal defaultMinPrincipal, BigDecimal defaultMaxPrincipal, BigDecimal defaultNominalInterestRatePerPeriod,
BigDecimal defaultMinNominalInterestRatePerPeriod, BigDecimal defaultMaxNominalInterestRatePerPeriod,
PeriodFrequencyType interestPeriodFrequencyType, BigDecimal defaultAnnualNominalInterestRate, InterestMethod interestMethod,
InterestCalculationPeriodMethod interestCalculationPeriodMethod, Integer repayEvery,
PeriodFrequencyType repaymentFrequencyType, Integer defaultNumberOfRepayments, AmortizationMethod amortizationMethod,
BigDecimal inArrearsTolerance) {
PeriodFrequencyType repaymentFrequencyType, Integer defaultNumberOfRepayments, Integer defaultMinNumberOfRepayments,
Integer defaultMaxNumberOfRepayments, AmortizationMethod amortizationMethod, BigDecimal inArrearsTolerance) {
return new LoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance);
return new LoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal,
defaultNominalInterestRatePerPeriod, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
repaymentFrequencyType, defaultNumberOfRepayments, defaultMinNumberOfRepayments, defaultMaxNumberOfRepayments,
amortizationMethod, inArrearsTolerance);
}
}