From f077733cfbcd179c2dff7213770473706efcc2b8 Mon Sep 17 00:00:00 2001 From: ashok-conflux Date: Wed, 3 Apr 2013 19:35:22 +0530 Subject: [PATCH] WIP: add min and max constraint to Principal amount --- ...5_add_min_max_principal_column_to_loan.sql | 7 +++ .../core/data/DataValidatorBuilder.java | 17 ++++++ .../portfolio/loanaccount/domain/Loan.java | 12 +++-- .../loanschedule/domain/LoanSchedule.java | 22 ++++---- .../service/LoanScheduleAssembler.java | 4 +- ...anApplicationCommandFromApiJsonHelper.java | 18 +++++++ .../loanaccount/service/LoanAssembler.java | 9 +++- .../loanproduct/data/LoanProductData.java | 31 +++++++++-- .../loanproduct/domain/LoanProduct.java | 26 +++++++--- .../domain/LoanProductRelatedDetail.java | 52 +++++++++++++++++-- .../exception/NotInMinMaxRangeException.java | 19 +++++++ ...ProductCommandFromApiJsonDeserializer.java | 37 +++++++++++-- .../LoanProductReadPlatformServiceImpl.java | 12 +++-- .../LoanProductRelatedDetailTestHelper.java | 27 ++++++---- 14 files changed, 243 insertions(+), 50 deletions(-) create mode 100644 mifosng-db/migrations/V5_add_min_max_principal_column_to_loan.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/exception/NotInMinMaxRangeException.java diff --git a/mifosng-db/migrations/V5_add_min_max_principal_column_to_loan.sql b/mifosng-db/migrations/V5_add_min_max_principal_column_to_loan.sql new file mode 100644 index 000000000..399724860 --- /dev/null +++ b/mifosng-db/migrations/V5_add_min_max_principal_column_to_loan.sql @@ -0,0 +1,7 @@ +ALTER TABLE `m_product_loan` + ADD COLUMN `min_principal_amount` DECIMAL(19,6) NOT NULL AFTER `principal_amount`, + ADD COLUMN `max_principal_amount` DECIMAL(19,6) NOT NULL AFTER `min_principal_amount`; + +ALTER TABLE `m_loan` + ADD COLUMN `min_principal_amount` DECIMAL(19,6) NOT NULL AFTER `principal_amount`, + ADD COLUMN `max_principal_amount` DECIMAL(19,6) NOT NULL AFTER `min_principal_amount`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java index 3f50991bb..2c5453c3d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java @@ -372,6 +372,23 @@ public class DataValidatorBuilder { return this; } + public DataValidatorBuilder inMinAndMaxAmountRange(final BigDecimal minimumAmount, final BigDecimal maximumAmount) { + if (minimumAmount != null && maximumAmount != null && value != null){ + BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString())); + if (amount.compareTo(minimumAmount) == -1 || amount.compareTo(maximumAmount) == 1) { + StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) + .append(".amount.is.not.within.min.max.range"); + StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter) .append(" amount ").append(amount) + .append(" must be between ").append(minimumAmount).append(" and ").append(maximumAmount).append(" ."); + ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), + defaultEnglishMessage.toString(), parameter, amount, minimumAmount, maximumAmount); + dataValidationErrors.add(error); + return this; + } + } + return this; + } + public DataValidatorBuilder comapareMinAndMaxOfTwoBigDecmimalNos(final BigDecimal min, final BigDecimal max) { if (min != null && max != null) if (max.compareTo(min) == -1) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 8fb3dacc5..a1df89754 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1317,6 +1317,8 @@ public class Loan extends AbstractPersistable { final LoanScheduleGenerator loanScheduleGenerator = new DefaultLoanScheduleGeneratorFactory().create(interestMethod); final BigDecimal principal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount(); + final BigDecimal minPrincipal = this.loanRepaymentScheduleDetail.getMinPrincipal().getAmount(); + final BigDecimal maxPrincipal = this.loanRepaymentScheduleDetail.getMaxPrincipal().getAmount(); final BigDecimal inArrearsTolerance = this.loanRepaymentScheduleDetail.getInArrearsTolerance().getAmount(); final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod(); final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType(); @@ -1331,11 +1333,11 @@ public class Loan extends AbstractPersistable { final Integer loanTermFrequency = this.termFrequency; final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType); - final LoanSchedule loanSchedule = new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, interestRatePerPeriod, - interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, - repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, loanTermFrequency, - loanTermPeriodFrequencyType, setOfLoanCharges(), this.getDisbursementDate(), this.getExpectedFirstRepaymentOnDate(), - this.getInterestChargedFromDate(), inArrearsTolerance); + final LoanSchedule loanSchedule = new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, minPrincipal, + maxPrincipal, interestRatePerPeriod, interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, + interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, + loanTermFrequency, loanTermPeriodFrequencyType, setOfLoanCharges(), this.getDisbursementDate(), + this.getExpectedFirstRepaymentOnDate(), this.getInterestChargedFromDate(), inArrearsTolerance); final LoanScheduleData generatedData = loanSchedule.generate(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java index 1a7cc3405..b0b66aa34 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java @@ -27,6 +27,8 @@ public final class LoanSchedule { private final LoanScheduleGenerator loanScheduleGenerator; private final ApplicationCurrency applicationCurrency; private final BigDecimal principal; + private final BigDecimal minPrincipal; + private final BigDecimal maxPrincipal; private final BigDecimal nominalInterestRatePerPeriod; private final PeriodFrequencyType interestRatePeriodFrequencyType; private final BigDecimal nominalAnnualInterestRate; @@ -46,17 +48,19 @@ public final class LoanSchedule { private final Set loanCharges; public LoanSchedule(final LoanScheduleGenerator loanScheduleGenerator, final ApplicationCurrency applicationCurrency, - final BigDecimal principal, final BigDecimal nominalInterestRatePerPeriod, - final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalAnnualInterestRate, - final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, - final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments, + final BigDecimal principal, final BigDecimal minPrincipal, final BigDecimal maxPrincipal, + final BigDecimal nominalInterestRatePerPeriod, 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 Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, final Set loanCharges, final LocalDate disbursementDate, - final LocalDate repaymentStartFromDate, final LocalDate interestChargedFromDate, - final BigDecimal inArrearsTolerance) { + final LocalDate repaymentStartFromDate, final LocalDate interestChargedFromDate, final BigDecimal inArrearsTolerance) { this.loanScheduleGenerator = loanScheduleGenerator; this.applicationCurrency = applicationCurrency; this.principal = principal; + this.minPrincipal = minPrincipal; + this.maxPrincipal = maxPrincipal; this.nominalInterestRatePerPeriod = nominalInterestRatePerPeriod; this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType; this.nominalAnnualInterestRate = nominalAnnualInterestRate; @@ -83,9 +87,9 @@ public final class LoanSchedule { public LoanProductRelatedDetail loanProductRelatedDetail() { final MonetaryCurrency currency = new MonetaryCurrency(applicationCurrency.getCode(), applicationCurrency.getDecimalPlaces()); - return LoanProductRelatedDetail.createFrom(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, - nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType, - numberOfRepayments, amortizationMethod, this.inArrearsTolerance); + return LoanProductRelatedDetail.createFrom(currency, principal, minPrincipal, maxPrincipal, nominalInterestRatePerPeriod, + interestRatePeriodFrequencyType, nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, + repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, this.inArrearsTolerance); } public Integer getLoanTermFrequency() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index ea9864079..1b41bf3a0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -72,6 +72,8 @@ public class LoanScheduleAssembler { final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(currency.getCode()); final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element); + 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 Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestRateFrequencyType", element); final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element); @@ -105,7 +107,7 @@ public class LoanScheduleAssembler { final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod); - return new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, interestRatePerPeriod, + return new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, loanTermFrequency, loanTermPeriodFrequencyType, loanCharges, expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate, inArrearsTolerance); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java index 5f0b6c380..73822ebba 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java @@ -22,6 +22,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; 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.LoanProduct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -591,4 +592,21 @@ public final class LoanApplicationCommandFromApiJsonHelper { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); } } + + public void validateMinMaxConstraintValues(final JsonElement element, final LoanProduct loanProduct){ + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan"); + + final BigDecimal minPrincipal = loanProduct.getMinPrincipalAmount().getAmount(); + final BigDecimal maxPrincipal = loanProduct.getMaxPrincipalAmount().getAmount(); + final String principalParameterName = "principal"; + + if (fromApiJsonHelper.parameterExists(principalParameterName, element)) { + final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(principalParameterName, element); + baseDataValidator.reset().parameter(principalParameterName).value(principal).notNull().positiveAmount().inMinAndMaxAmountRange(minPrincipal, maxPrincipal); + } + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java index 87b423c01..e6efaf3b0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java @@ -42,6 +42,7 @@ 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; @@ -68,6 +69,7 @@ public class LoanAssembler { private final FromJsonHelper fromApiJsonHelper; private final LoanSummaryWrapper loanSummaryWrapper; private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory; + private final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer; @Autowired public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository, @@ -76,7 +78,8 @@ public class LoanAssembler { final StaffRepository staffRepository, final CodeValueRepositoryWrapper codeValueRepository, final LoanScheduleAssembler loanScheduleAssembler, final LoanChargeAssembler loanChargeAssembler, final CollateralAssembler loanCollateralAssembler, final LoanSummaryWrapper loanSummaryWrapper, - final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory) { + final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory, + final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer) { this.fromApiJsonHelper = fromApiJsonHelper; this.loanProductRepository = loanProductRepository; this.clientRepository = clientRepository; @@ -90,6 +93,7 @@ public class LoanAssembler { this.loanCollateralAssembler = loanCollateralAssembler; this.loanSummaryWrapper = loanSummaryWrapper; this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory; + this.fromApiJsonDeserializer = fromApiJsonDeserializer; } public Loan assembleFrom(final JsonCommand command, final AppUser currentUser) { @@ -113,6 +117,9 @@ public class LoanAssembler { final LoanProduct loanProduct = this.loanProductRepository.findOne(productId); if (loanProduct == null) { throw new LoanProductNotFoundException(productId); } + //validate min and max constraint parameters + this.fromApiJsonDeserializer.validateMinMaxConstraintValues(element, loanProduct); + final Fund fund = findFundByIdIfProvided(fundId); final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId); final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java index c61976aa9..e8b84b010 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java @@ -37,6 +37,8 @@ public class LoanProductData { // terms private final CurrencyData currency; private final BigDecimal principal; + private BigDecimal minPrincipal; + private BigDecimal maxPrincipal; private final Integer numberOfRepayments; private final Integer repaymentEvery; private final EnumOptionData repaymentFrequencyType; @@ -80,6 +82,8 @@ public class LoanProductData { final String description = null; final CurrencyData currency = null; final BigDecimal principal = null; + final BigDecimal minPrincipal = null; + final BigDecimal maxPrincipal = null; final BigDecimal tolerance = null; final Integer numberOfRepayments = null; final Integer repaymentEvery = null; @@ -97,7 +101,7 @@ public class LoanProductData { final Collection charges = null; final EnumOptionData accountingType = null; - return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery, + return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, repaymentEvery, interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType); @@ -106,6 +110,8 @@ public class LoanProductData { public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) { final String description = null; final BigDecimal principal = null; + final BigDecimal minPrincipal = null; + final BigDecimal maxPrincipal = null; final BigDecimal tolerance = null; final Integer numberOfRepayments = null; final Integer repaymentEvery = null; @@ -123,7 +129,7 @@ public class LoanProductData { final Collection charges = null; final EnumOptionData accountingType = null; - return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery, + return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, repaymentEvery, interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType); @@ -135,6 +141,8 @@ public class LoanProductData { final String description = null; final CurrencyData currency = CurrencyData.blank(); final BigDecimal principal = null; + final BigDecimal minPrincipal = null; + final BigDecimal maxPrincipal = null; final BigDecimal tolerance = null; final Integer numberOfRepayments = null; final Integer repaymentEvery = null; @@ -153,7 +161,7 @@ public class LoanProductData { final Collection charges = null; final EnumOptionData accountingType = LoanEnumerations.accountingRuleType(AccountingRuleType.NONE); - return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery, + return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, repaymentEvery, interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType); @@ -169,8 +177,9 @@ public class LoanProductData { } public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency, - final BigDecimal principal, final BigDecimal tolerance, final Integer numberOfRepayments, final Integer repaymentEvery, - final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType, + 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 EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType, final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName, final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName, @@ -180,6 +189,8 @@ public class LoanProductData { this.description = description; this.currency = currency; this.principal = principal; + this.minPrincipal = minPrincipal; + this.maxPrincipal = maxPrincipal; this.inArrearsTolerance = tolerance; this.numberOfRepayments = numberOfRepayments; this.repaymentEvery = repaymentEvery; @@ -227,6 +238,8 @@ public class LoanProductData { this.fundName = productData.fundName; this.principal = productData.principal; + this.minPrincipal = productData.minPrincipal; + this.maxPrincipal = productData.maxPrincipal; this.inArrearsTolerance = productData.inArrearsTolerance; this.numberOfRepayments = productData.numberOfRepayments; this.repaymentEvery = productData.repaymentEvery; @@ -337,6 +350,14 @@ public class LoanProductData { return principal; } + public BigDecimal getMinPrincipal() { + return this.minPrincipal; + } + + public BigDecimal getMaxPrincipal() { + return this.maxPrincipal; + } + public BigDecimal getInArrearsTolerance() { return inArrearsTolerance; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 8bdcd386b..d2f48a1ec 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -24,6 +24,7 @@ import javax.persistence.UniqueConstraint; import org.apache.commons.lang.StringUtils; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.charge.domain.Charge; import org.mifosplatform.portfolio.fund.domain.Fund; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator; @@ -78,6 +79,8 @@ public class LoanProduct extends AbstractPersistable { final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal); final BigDecimal principal = command.bigDecimalValueOfParameterNamed("principal"); + final BigDecimal minPrincipal = command.bigDecimalValueOfParameterNamed("minPrincipal"); + final BigDecimal maxPrincipal = command.bigDecimalValueOfParameterNamed("maxPrincipal"); final InterestMethod interestMethod = InterestMethod.fromInt(command.integerValueOfParameterNamed("interestType")); final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command @@ -95,9 +98,10 @@ public class LoanProduct extends AbstractPersistable { final BigDecimal inArrearsTolerance = command.bigDecimalValueOfParameterNamed("inArrearsTolerance"); final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule")); - return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, interestRatePerPeriod, - interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, - repaymentFrequencyType, numberOfRepayments, amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType); + return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal, + interestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, + repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationMethod, inArrearsTolerance, productCharges, + accountingRuleType); } protected LoanProduct() { @@ -106,6 +110,7 @@ public class LoanProduct extends AbstractPersistable { 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 defaultAnnualNominalInterestRate, final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery, @@ -125,9 +130,10 @@ public class LoanProduct extends AbstractPersistable { this.charges = charges; } - this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, - interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, - repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, inArrearsTolerance); + this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, + defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, + interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, + inArrearsTolerance); if (accountingRuleType != null) { this.accountingRule = accountingRuleType.getValue(); @@ -227,4 +233,12 @@ public class LoanProduct extends AbstractPersistable { public boolean isAccrualBasedAccountingEnabled() { return AccountingRuleType.ACCRUAL_BASED.getValue().equals(this.accountingRule); } + + public Money getMinPrincipalAmount(){ + return this.loanProductRelatedDetail.getMinPrincipal(); + } + + public Money getMaxPrincipalAmount(){ + return this.loanProductRelatedDetail.getMaxPrincipal(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java index f861259f4..e41b3a02e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java @@ -20,6 +20,7 @@ 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 @@ -33,6 +34,12 @@ 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) + private BigDecimal minPrincipal; + + @Column(name = "max_principal_amount", scale = 6, precision = 19, nullable = false) + private BigDecimal maxPrincipal; @Column(name = "nominal_interest_rate_per_period", scale = 6, precision = 19, nullable = false) private BigDecimal nominalInterestRatePerPeriod; @@ -80,13 +87,13 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche private BigDecimal inArrearsTolerance; public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal, - final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType, - final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod, - final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repaymentEvery, - final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments, + final BigDecimal minPrincipal, final BigDecimal maxPrincipal, final BigDecimal nominalInterestRatePerPeriod, + 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) { - return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, + return new LoanProductRelatedDetail(currency, principal, minPrincipal, maxPrincipal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, inArrearsTolerance); } @@ -96,6 +103,7 @@ 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 defaultAnnualNominalInterestRate, final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery, @@ -103,6 +111,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance) { this.currency = currency; this.principal = defaultPrincipal; + this.minPrincipal = defaultMinPrincipal; + this.maxPrincipal = defaultMaxPrincipal; this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod; this.interestPeriodFrequencyType = interestPeriodFrequencyType; this.annualNominalInterestRate = defaultAnnualNominalInterestRate; @@ -127,6 +137,14 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche return Money.of(this.currency, this.principal); } + public Money getMinPrincipal() { + return Money.of(this.currency, this.minPrincipal); + } + + public Money getMaxPrincipal() { + return Money.of(this.currency, this.maxPrincipal); + } + public Money getInArrearsTolerance() { return Money.of(this.currency, this.inArrearsTolerance); } @@ -208,12 +226,29 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final String localeAsInput = command.locale(); + final String minPrincipalParamName = "minPrincipal"; + if (command.isChangeInBigDecimalParameterNamed(minPrincipalParamName, this.minPrincipal)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minPrincipalParamName); + actualChanges.put(minPrincipalParamName, newValue); + actualChanges.put("locale", localeAsInput); + this.minPrincipal = newValue; + } + + final String maxPrincipalParamName = "maxPrincipal"; + if (command.isChangeInBigDecimalParameterNamed(maxPrincipalParamName, this.maxPrincipal)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(maxPrincipalParamName); + actualChanges.put(maxPrincipalParamName, newValue); + actualChanges.put("locale", localeAsInput); + this.maxPrincipal = newValue; + } + final String principalParamName = "principal"; if (command.isChangeInBigDecimalParameterNamed(principalParamName, this.principal)) { final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(principalParamName); actualChanges.put(principalParamName, newValue); actualChanges.put("locale", localeAsInput); this.principal = newValue; + validatePrincipalAmount(); } final String repaymentEveryParamName = "repaymentEvery"; @@ -294,6 +329,13 @@ 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); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/exception/NotInMinMaxRangeException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/exception/NotInMinMaxRangeException.java new file mode 100644 index 000000000..502b2f30d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/exception/NotInMinMaxRangeException.java @@ -0,0 +1,19 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanproduct.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * {@link AbstractPlatformDomainRuleException} thrown when a value is not within minimum and maximum values. + * + */ +public class NotInMinMaxRangeException extends AbstractPlatformDomainRuleException { + + public NotInMinMaxRangeException(final String entity, final String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) { + super("validation.msg." + entity + "." + postFix + ".is.not.within.min.max.range", defaultUserMessage, defaultUserMessageArgs); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductCommandFromApiJsonDeserializer.java index 51a571ca8..64fe5cf63 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductCommandFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductCommandFromApiJsonDeserializer.java @@ -36,7 +36,7 @@ public final class LoanProductCommandFromApiJsonDeserializer { * The parameters supported for this command. */ private final Set supportedParameters = new HashSet(Arrays.asList("locale", "name", "description", "fundId", - "currencyCode", "digitsAfterDecimal", "principal", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", + "currencyCode", "digitsAfterDecimal", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "charges", "accountingRule", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), @@ -81,9 +81,15 @@ 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(); - + baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount().inMinAndMaxAmountRange(minPrincipal, maxPrincipal); + final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element); baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero(); @@ -212,10 +218,33 @@ public final class LoanProductCommandFromApiJsonDeserializer { final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault()); baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); } + + String minPrincipalParameterName = "minPrincipal"; + BigDecimal minPrincipalAmount = null; + if(fromApiJsonHelper.parameterExists(minPrincipalParameterName, element)){ + minPrincipalAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minPrincipalParameterName, element); + } + + 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(); + } if (fromApiJsonHelper.parameterExists("principal", element)) { final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element); - baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount(); + baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount().inMinAndMaxAmountRange(minPrincipalAmount, maxPrincipalAmount); } if (fromApiJsonHelper.parameterExists("inArrearsTolerance", element)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index 5e33d267b..a464026ce 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -96,7 +96,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo public String loanProductSchema() { 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.currency_code as currencyCode, lp.currency_digits as currencyDigits, " + + "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.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, " @@ -130,6 +130,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo currencyNameCode); final BigDecimal principal = rs.getBigDecimal("principal"); + final BigDecimal minPrincipal = rs.getBigDecimal("minPrincipal"); + final BigDecimal maxPrincipal = rs.getBigDecimal("maxPrincipal"); final BigDecimal tolerance = rs.getBigDecimal("tolerance"); final Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments"); @@ -157,10 +159,10 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final EnumOptionData interestCalculationPeriodType = LoanEnumerations .interestCalculationPeriodType(interestCalculationPeriodTypeId); - return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery, - interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, - interestType, interestCalculationPeriodType, fundId, fundName, transactionStrategyId, transactionStrategyName, - this.charges, accountingRuleType); + return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, + numberOfRepayments, repaymentEvery, interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, + interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, + transactionStrategyId, transactionStrategyName, this.charges, accountingRuleType); } } diff --git a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/LoanProductRelatedDetailTestHelper.java b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/LoanProductRelatedDetailTestHelper.java index 5a40c5d7a..84558cc8b 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/LoanProductRelatedDetailTestHelper.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/LoanProductRelatedDetailTestHelper.java @@ -21,6 +21,8 @@ public class LoanProductRelatedDetailTestHelper { MonetaryCurrency currency = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build(); BigDecimal defaultPrincipal = BigDecimal.valueOf(Double.valueOf("200000")); + BigDecimal defaultMinPrincipal = BigDecimal.valueOf(Double.valueOf("100000")); + BigDecimal defaultMaxPrincipal = BigDecimal.valueOf(Double.valueOf("250000")); // 2% per month, 24% per year BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2")); @@ -37,7 +39,7 @@ public class LoanProductRelatedDetailTestHelper { BigDecimal inArrearsTolerance = BigDecimal.ZERO; - return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, + return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance); } @@ -45,6 +47,8 @@ public class LoanProductRelatedDetailTestHelper { public static LoanProductRelatedDetail createSettingsForEqualInstallmentAmortizationQuarterly() { MonetaryCurrency currency = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build(); BigDecimal defaultPrincipal = BigDecimal.valueOf(Double.valueOf("200000")); + BigDecimal defaultMinPrincipal = BigDecimal.valueOf(Double.valueOf("100000")); + BigDecimal defaultMaxPrincipal = BigDecimal.valueOf(Double.valueOf("250000")); // 2% per month, 24% per year BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2")); @@ -61,7 +65,7 @@ public class LoanProductRelatedDetailTestHelper { BigDecimal inArrearsTolerance = BigDecimal.ZERO; - return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, + return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance); } @@ -70,6 +74,8 @@ public class LoanProductRelatedDetailTestHelper { MonetaryCurrency currency = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build(); BigDecimal defaultPrincipal = BigDecimal.valueOf(Double.valueOf("200000")); + BigDecimal defaultMinPrincipal = BigDecimal.valueOf(Double.valueOf("200000")); + BigDecimal defaultMaxPrincipal = BigDecimal.valueOf(Double.valueOf("200000")); // 2% per month, 24% per year BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2")); @@ -85,7 +91,7 @@ public class LoanProductRelatedDetailTestHelper { BigDecimal inArrearsTolerance = BigDecimal.ZERO; - return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, + return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance); } @@ -94,6 +100,8 @@ public class LoanProductRelatedDetailTestHelper { MonetaryCurrency currency = new MonetaryCurrencyBuilder().withCode("KSH").withDigitsAfterDecimal(0).build(); BigDecimal defaultPrincipal = BigDecimal.valueOf(Double.valueOf("15000")); + BigDecimal defaultMinPrincipal = BigDecimal.valueOf(Double.valueOf("15000")); + BigDecimal defaultMaxPrincipal = BigDecimal.valueOf(Double.valueOf("15000")); // 2% per month, 24% per year BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.valueOf("2")); @@ -111,19 +119,20 @@ public class LoanProductRelatedDetailTestHelper { AmortizationMethod amortizationMethod = AmortizationMethod.EQUAL_PRINCIPAL; - return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, - defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, - defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance); + return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, + defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, + interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfRepayments, amortizationMethod, + inArrearsTolerance); } private static LoanProductRelatedDetail createLoanProductRelatedDetail(MonetaryCurrency currency, BigDecimal defaultPrincipal, - BigDecimal defaultNominalInterestRatePerPeriod, PeriodFrequencyType interestPeriodFrequencyType, - BigDecimal defaultAnnualNominalInterestRate, InterestMethod interestMethod, + BigDecimal defaultMinPrincipal, BigDecimal defaultMaxPrincipal, BigDecimal defaultNominalInterestRatePerPeriod, + PeriodFrequencyType interestPeriodFrequencyType, BigDecimal defaultAnnualNominalInterestRate, InterestMethod interestMethod, InterestCalculationPeriodMethod interestCalculationPeriodMethod, Integer repayEvery, PeriodFrequencyType repaymentFrequencyType, Integer defaultNumberOfRepayments, AmortizationMethod amortizationMethod, BigDecimal inArrearsTolerance) { - return new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, + return new LoanProductRelatedDetail(currency, defaultPrincipal, defaultMinPrincipal, defaultMaxPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType, defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance); }