| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.fineract.portfolio.loanproduct.serialization; |
| |
| import com.google.gson.JsonArray; |
| import com.google.gson.JsonElement; |
| import com.google.gson.JsonObject; |
| import com.google.gson.reflect.TypeToken; |
| import java.lang.reflect.Type; |
| import java.math.BigDecimal; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams; |
| import org.apache.fineract.accounting.common.AccountingRuleType; |
| import org.apache.fineract.infrastructure.core.data.ApiParameterError; |
| import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; |
| import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; |
| import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; |
| import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; |
| import org.apache.fineract.portfolio.calendar.service.CalendarUtils; |
| import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; |
| import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; |
| import org.apache.fineract.portfolio.loanproduct.LoanProductConstants; |
| import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; |
| import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; |
| import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; |
| import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; |
| import org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCalculationStrategy; |
| import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; |
| import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType; |
| import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType; |
| import org.apache.fineract.portfolio.loanproduct.exception.EqualAmortizationUnsupportedFeatureException; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Component; |
| |
| @Component |
| public final class LoanProductDataValidator { |
| |
| /** |
| * The parameters supported for this command. |
| */ |
| private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("locale", "dateFormat", "name", "description", "fundId", |
| "currencyCode", "digitsAfterDecimal", "inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", |
| "numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", |
| "minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", |
| "interestCalculationPeriodType", LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, |
| "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment", "recurringMoratoriumOnPrincipalPeriods", |
| "graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule", "includeInBorrowerCycle", "startDate", |
| "closeDate", "externalId", "isLinkedToFloatingInterestRates", "floatingRatesId", "interestRateDifferential", |
| "minDifferentialLendingRate", "defaultDifferentialLendingRate", "maxDifferentialLendingRate", |
| "isFloatingInterestRateCalculationAllowed", "syncExpectedWithDisbursementDate", "scorecardFeatures", |
| LoanProductAccountingParams.FEES_RECEIVABLE.getValue(), LoanProductAccountingParams.FUND_SOURCE.getValue(), |
| LoanProductAccountingParams.INCOME_FROM_FEES.getValue(), LoanProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), |
| LoanProductAccountingParams.INTEREST_ON_LOANS.getValue(), LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue(), |
| LoanProductAccountingParams.LOAN_PORTFOLIO.getValue(), LoanProductAccountingParams.OVERPAYMENT.getValue(), |
| LoanProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), |
| LoanProductAccountingParams.PENALTIES_RECEIVABLE.getValue(), |
| LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), |
| LoanProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue(), LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue(), |
| LoanProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.PRINCIPAL_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.INTEREST_RATE_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.NUMBER_OF_REPAYMENT_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME, LoanProductConstants.SHORT_NAME, |
| LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME, LoanProductConstants.OUTSTANDING_LOAN_BALANCE_PARAMETER_NAME, |
| LoanProductConstants.MAX_TRANCHE_COUNT_PARAMETER_NAME, LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, |
| LoanProductConstants.OVERDUE_DAYS_FOR_NPA_PARAMETER_NAME, LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME, |
| LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME, LoanProductConstants.DAYS_IN_MONTH_TYPE_PARAMETER_NAME, |
| LoanProductConstants.rescheduleStrategyMethodParameterName, |
| LoanProductConstants.interestRecalculationCompoundingMethodParameterName, |
| LoanProductConstants.recalculationRestFrequencyIntervalParameterName, |
| LoanProductConstants.recalculationRestFrequencyTypeParameterName, |
| LoanProductConstants.recalculationCompoundingFrequencyIntervalParameterName, |
| LoanProductConstants.recalculationCompoundingFrequencyTypeParameterName, |
| LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName, |
| LoanProductConstants.MINIMUM_DAYS_BETWEEN_DISBURSAL_AND_FIRST_REPAYMENT, LoanProductConstants.mandatoryGuaranteeParamName, |
| LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName, |
| LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstallmentParamName, |
| LoanProductConstants.ACCOUNT_MOVES_OUT_OF_NPA_ONLY_ON_ARREARS_COMPLETION_PARAM_NAME, |
| LoanProductConstants.canDefineEmiAmountParamName, LoanProductConstants.installmentAmountInMultiplesOfParamName, |
| LoanProductConstants.preClosureInterestCalculationStrategyParamName, LoanProductConstants.allowAttributeOverridesParamName, |
| LoanProductConstants.allowVariableInstallmentsParamName, LoanProductConstants.minimumGapBetweenInstallments, |
| LoanProductConstants.maximumGapBetweenInstallments, LoanProductConstants.recalculationCompoundingFrequencyWeekdayParamName, |
| LoanProductConstants.recalculationCompoundingFrequencyNthDayParamName, |
| LoanProductConstants.recalculationCompoundingFrequencyOnDayParamName, |
| LoanProductConstants.recalculationRestFrequencyWeekdayParamName, LoanProductConstants.recalculationRestFrequencyNthDayParamName, |
| LoanProductConstants.recalculationRestFrequencyOnDayParamName, |
| LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName, |
| LoanProductConstants.CAN_USE_FOR_TOPUP, LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, LoanProductConstants.RATES_PARAM_NAME, |
| LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName, LoanProductConstants.DISALLOW_EXPECTED_DISBURSEMENTS, |
| LoanProductConstants.ALLOW_APPROVED_DISBURSED_AMOUNTS_OVER_APPLIED, LoanProductConstants.OVER_APPLIED_CALCULATION_TYPE, |
| LoanProductConstants.OVER_APPLIED_NUMBER)); |
| |
| private static final String[] supportedloanConfigurableAttributes = { LoanProductConstants.amortizationTypeParamName, |
| LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyIdParamName, |
| LoanProductConstants.interestCalculationPeriodTypeParamName, LoanProductConstants.inArrearsToleranceParamName, |
| LoanProductConstants.repaymentEveryParamName, LoanProductConstants.graceOnPrincipalAndInterestPaymentParamName, |
| LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME }; |
| |
| private final FromJsonHelper fromApiJsonHelper; |
| |
| @Autowired |
| public LoanProductDataValidator(final FromJsonHelper fromApiJsonHelper) { |
| this.fromApiJsonHelper = fromApiJsonHelper; |
| } |
| |
| public void validateForCreate(final String json) { |
| if (StringUtils.isBlank(json)) { |
| throw new InvalidJsonException(); |
| } |
| |
| final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType(); |
| this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); |
| |
| final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); |
| final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanproduct"); |
| |
| final JsonElement element = this.fromApiJsonHelper.parse(json); |
| |
| final String name = this.fromApiJsonHelper.extractStringNamed("name", element); |
| baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100); |
| |
| final String shortName = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.SHORT_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.SHORT_NAME).value(shortName).notBlank().notExceedingLengthOf(4); |
| |
| final String description = this.fromApiJsonHelper.extractStringNamed("description", element); |
| baseDataValidator.reset().parameter("description").value(description).notExceedingLengthOf(500); |
| |
| if (this.fromApiJsonHelper.parameterExists("fundId", element)) { |
| final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element); |
| baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero(); |
| } |
| |
| boolean isEqualAmortization = false; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, element)) { |
| isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM).value(isEqualAmortization).ignoreIfNull() |
| .validateForBooleanValue(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.MINIMUM_DAYS_BETWEEN_DISBURSAL_AND_FIRST_REPAYMENT, element)) { |
| final Long minimumDaysBetweenDisbursalAndFirstRepayment = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductConstants.MINIMUM_DAYS_BETWEEN_DISBURSAL_AND_FIRST_REPAYMENT, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.MINIMUM_DAYS_BETWEEN_DISBURSAL_AND_FIRST_REPAYMENT) |
| .value(minimumDaysBetweenDisbursalAndFirstRepayment).ignoreIfNull().integerGreaterThanZero(); |
| } |
| |
| final Boolean includeInBorrowerCycle = this.fromApiJsonHelper.extractBooleanNamed("includeInBorrowerCycle", element); |
| baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull() |
| .validateForBooleanValue(); |
| |
| // terms |
| final String currencyCode = this.fromApiJsonHelper.extractStringNamed("currencyCode", element); |
| baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3); |
| |
| final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); |
| |
| final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerNamed("inMultiplesOf", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("inMultiplesOf").value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater(); |
| |
| final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element); |
| baseDataValidator.reset().parameter("principal").value(principal).positiveAmount(); |
| |
| final String minPrincipalParameterName = "minPrincipal"; |
| BigDecimal minPrincipalAmount = null; |
| if (this.fromApiJsonHelper.parameterExists(minPrincipalParameterName, element)) { |
| minPrincipalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minPrincipalParameterName, element); |
| baseDataValidator.reset().parameter(minPrincipalParameterName).value(minPrincipalAmount).ignoreIfNull().positiveAmount(); |
| } |
| |
| final String maxPrincipalParameterName = "maxPrincipal"; |
| BigDecimal maxPrincipalAmount = null; |
| if (this.fromApiJsonHelper.parameterExists(maxPrincipalParameterName, element)) { |
| maxPrincipalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxPrincipalParameterName, element); |
| baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).ignoreIfNull().positiveAmount(); |
| } |
| |
| if (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) >= 0) { |
| |
| if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) >= 0) { |
| baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).notLessThanMin(minPrincipalAmount); |
| if (minPrincipalAmount.compareTo(maxPrincipalAmount) <= 0 && principal != null) { |
| baseDataValidator.reset().parameter("principal").value(principal).inMinAndMaxAmountRange(minPrincipalAmount, |
| maxPrincipalAmount); |
| } |
| } else if (principal != null) { |
| baseDataValidator.reset().parameter("principal").value(principal).notGreaterThanMax(maxPrincipalAmount); |
| } |
| } else if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) >= 0 && principal != null) { |
| baseDataValidator.reset().parameter("principal").value(principal).notLessThanMin(minPrincipalAmount); |
| } |
| |
| final Integer numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element); |
| baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero(); |
| |
| final String minNumberOfRepaymentsParameterName = "minNumberOfRepayments"; |
| Integer minNumberOfRepayments = null; |
| if (this.fromApiJsonHelper.parameterExists(minNumberOfRepaymentsParameterName, element)) { |
| minNumberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(minNumberOfRepaymentsParameterName, element); |
| baseDataValidator.reset().parameter(minNumberOfRepaymentsParameterName).value(minNumberOfRepayments).ignoreIfNull() |
| .integerGreaterThanZero(); |
| } |
| |
| final String maxNumberOfRepaymentsParameterName = "maxNumberOfRepayments"; |
| Integer maxNumberOfRepayments = null; |
| if (this.fromApiJsonHelper.parameterExists(maxNumberOfRepaymentsParameterName, element)) { |
| maxNumberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(maxNumberOfRepaymentsParameterName, element); |
| baseDataValidator.reset().parameter(maxNumberOfRepaymentsParameterName).value(maxNumberOfRepayments).ignoreIfNull() |
| .integerGreaterThanZero(); |
| } |
| |
| if (maxNumberOfRepayments != null && maxNumberOfRepayments.compareTo(0) > 0) { |
| if (minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) > 0) { |
| 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) > 0) { |
| baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notLessThanMin(minNumberOfRepayments); |
| } |
| |
| final Integer repaymentEvery = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element); |
| baseDataValidator.reset().parameter("repaymentEvery").value(repaymentEvery).notNull().integerGreaterThanZero(); |
| |
| final Integer repaymentFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("repaymentFrequencyType", element, |
| Locale.getDefault()); |
| baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3); |
| |
| // settings |
| final Integer amortizationType = this.fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1); |
| |
| final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed("interestType", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("interestType").value(interestType).notNull().inMinMaxRange(0, 1); |
| |
| final Integer interestCalculationPeriodType = this.fromApiJsonHelper.extractIntegerNamed("interestCalculationPeriodType", element, |
| Locale.getDefault()); |
| baseDataValidator.reset().parameter("interestCalculationPeriodType").value(interestCalculationPeriodType).notNull().inMinMaxRange(0, |
| 1); |
| |
| final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element); |
| baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount(); |
| |
| final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element); |
| baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull() |
| .integerGreaterThanZero(); |
| |
| // grace validation |
| final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element); |
| baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(graceOnPrincipalPayment).zeroOrPositiveAmount(); |
| |
| final Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element); |
| baseDataValidator.reset().parameter("graceOnInterestPayment").value(graceOnInterestPayment).zeroOrPositiveAmount(); |
| |
| final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element); |
| baseDataValidator.reset().parameter("graceOnInterestCharged").value(graceOnInterestCharged).zeroOrPositiveAmount(); |
| |
| final Integer graceOnArrearsAgeing = this.fromApiJsonHelper |
| .extractIntegerWithLocaleNamed(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME).value(graceOnArrearsAgeing) |
| .integerZeroOrGreater(); |
| |
| final Integer overdueDaysForNPA = this.fromApiJsonHelper |
| .extractIntegerWithLocaleNamed(LoanProductConstants.OVERDUE_DAYS_FOR_NPA_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.OVERDUE_DAYS_FOR_NPA_PARAMETER_NAME).value(overdueDaysForNPA) |
| .integerZeroOrGreater(); |
| |
| /** |
| * { @link DaysInYearType } |
| */ |
| final Integer daysInYearType = this.fromApiJsonHelper.extractIntegerNamed(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME, |
| element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME).value(daysInYearType).notNull() |
| .isOneOfTheseValues(1, 360, 364, 365); |
| |
| /** |
| * { @link DaysInMonthType } |
| */ |
| final Integer daysInMonthType = this.fromApiJsonHelper.extractIntegerNamed(LoanProductConstants.DAYS_IN_MONTH_TYPE_PARAMETER_NAME, |
| element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.DAYS_IN_MONTH_TYPE_PARAMETER_NAME).value(daysInMonthType).notNull() |
| .isOneOfTheseValues(1, 30); |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ACCOUNT_MOVES_OUT_OF_NPA_ONLY_ON_ARREARS_COMPLETION_PARAM_NAME, |
| element)) { |
| Boolean npaChangeConfig = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.ACCOUNT_MOVES_OUT_OF_NPA_ONLY_ON_ARREARS_COMPLETION_PARAM_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.ACCOUNT_MOVES_OUT_OF_NPA_ONLY_ON_ARREARS_COMPLETION_PARAM_NAME) |
| .value(npaChangeConfig).notNull().isOneOfTheseValues(true, false); |
| } |
| |
| // Interest recalculation settings |
| final Boolean isInterestRecalculationEnabled = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME) |
| .value(isInterestRecalculationEnabled).notNull().isOneOfTheseValues(true, false); |
| |
| if (isInterestRecalculationEnabled != null) { |
| if (isInterestRecalculationEnabled) { |
| if (isEqualAmortization) { |
| throw new EqualAmortizationUnsupportedFeatureException("interest.recalculation", "interest recalculation"); |
| } |
| validateInterestRecalculationParams(element, baseDataValidator, null); |
| } |
| } |
| |
| // interest rates |
| if (this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element) |
| && this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true) { |
| if (isEqualAmortization) { |
| throw new EqualAmortizationUnsupportedFeatureException("floating.interest.rate", "floating interest rate"); |
| } |
| if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) { |
| baseDataValidator.reset().parameter("interestRatePerPeriod").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.true", |
| "interestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("minInterestRatePerPeriod", element)) { |
| baseDataValidator.reset().parameter("minInterestRatePerPeriod").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.true", |
| "minInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("maxInterestRatePerPeriod", element)) { |
| baseDataValidator.reset().parameter("maxInterestRatePerPeriod").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.true", |
| "maxInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) { |
| baseDataValidator.reset().parameter("interestRateFrequencyType").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.true", |
| "interestRateFrequencyType param is not supported when isLinkedToFloatingInterestRates is true"); |
| } |
| if ((interestType == null || !interestType.equals(InterestMethod.DECLINING_BALANCE.getValue())) |
| || (isInterestRecalculationEnabled == null || isInterestRecalculationEnabled == false)) { |
| baseDataValidator.reset().parameter("isLinkedToFloatingInterestRates").failWithCode( |
| "supported.only.for.declining.balance.interest.recalculation.enabled", |
| "Floating interest rates are supported only for declining balance and interest recalculation enabled loan products"); |
| } |
| |
| final Integer floatingRatesId = this.fromApiJsonHelper.extractIntegerNamed("floatingRatesId", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("floatingRatesId").value(floatingRatesId).notNull(); |
| |
| final BigDecimal interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRateDifferential", |
| element); |
| baseDataValidator.reset().parameter("interestRateDifferential").value(interestRateDifferential).notNull() |
| .zeroOrPositiveAmount(); |
| |
| final String minDifferentialLendingRateParameterName = "minDifferentialLendingRate"; |
| BigDecimal minDifferentialLendingRate = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(minDifferentialLendingRateParameterName, element); |
| baseDataValidator.reset().parameter(minDifferentialLendingRateParameterName).value(minDifferentialLendingRate).notNull() |
| .zeroOrPositiveAmount(); |
| |
| final String defaultDifferentialLendingRateParameterName = "defaultDifferentialLendingRate"; |
| BigDecimal defaultDifferentialLendingRate = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(defaultDifferentialLendingRateParameterName, element); |
| baseDataValidator.reset().parameter(defaultDifferentialLendingRateParameterName).value(defaultDifferentialLendingRate).notNull() |
| .zeroOrPositiveAmount(); |
| |
| final String maxDifferentialLendingRateParameterName = "maxDifferentialLendingRate"; |
| BigDecimal maxDifferentialLendingRate = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(maxDifferentialLendingRateParameterName, element); |
| baseDataValidator.reset().parameter(maxDifferentialLendingRateParameterName).value(maxDifferentialLendingRate).notNull() |
| .zeroOrPositiveAmount(); |
| |
| if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| baseDataValidator.reset().parameter("defaultDifferentialLendingRate").value(defaultDifferentialLendingRate) |
| .notLessThanMin(minDifferentialLendingRate); |
| } |
| } |
| |
| if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate) |
| .notLessThanMin(minDifferentialLendingRate); |
| } |
| } |
| |
| if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate) |
| .notLessThanMin(defaultDifferentialLendingRate); |
| } |
| } |
| |
| final Boolean isFloatingInterestRateCalculationAllowed = this.fromApiJsonHelper |
| .extractBooleanNamed("isFloatingInterestRateCalculationAllowed", element); |
| baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").value(isFloatingInterestRateCalculationAllowed) |
| .notNull().isOneOfTheseValues(true, false); |
| } else { |
| if (this.fromApiJsonHelper.parameterExists("floatingRatesId", element)) { |
| baseDataValidator.reset().parameter("floatingRatesId").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "floatingRatesId param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)) { |
| baseDataValidator.reset().parameter("interestRateDifferential").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "interestRateDifferential param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("minDifferentialLendingRate", element)) { |
| baseDataValidator.reset().parameter("minDifferentialLendingRate").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "minDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("defaultDifferentialLendingRate", element)) { |
| baseDataValidator.reset().parameter("defaultDifferentialLendingRate").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "defaultDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("maxDifferentialLendingRate", element)) { |
| baseDataValidator.reset().parameter("maxDifferentialLendingRate").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "maxDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)) { |
| baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", |
| element); |
| baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount(); |
| |
| final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod"; |
| BigDecimal minInterestRatePerPeriod = null; |
| if (this.fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) { |
| minInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName, |
| element); |
| baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull() |
| .zeroOrPositiveAmount(); |
| } |
| |
| final String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod"; |
| BigDecimal maxInterestRatePerPeriod = null; |
| if (this.fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) { |
| maxInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName, |
| element); |
| baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull() |
| .zeroOrPositiveAmount(); |
| } |
| |
| if (maxInterestRatePerPeriod != null && maxInterestRatePerPeriod.compareTo(BigDecimal.ZERO) >= 0) { |
| if (minInterestRatePerPeriod != null && minInterestRatePerPeriod.compareTo(BigDecimal.ZERO) >= 0) { |
| 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) >= 0) { |
| baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod) |
| .notLessThanMin(minInterestRatePerPeriod); |
| } |
| |
| final Integer interestRateFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element, |
| Locale.getDefault()); |
| baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 4); |
| } |
| |
| // Guarantee Funds |
| Boolean holdGuaranteeFunds = false; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.holdGuaranteeFundsParamName, element)) { |
| holdGuaranteeFunds = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.holdGuaranteeFundsParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.holdGuaranteeFundsParamName).value(holdGuaranteeFunds).notNull() |
| .isOneOfTheseValues(true, false); |
| } |
| |
| if (holdGuaranteeFunds != null) { |
| if (holdGuaranteeFunds) { |
| validateGuaranteeParams(element, baseDataValidator, null); |
| } |
| } |
| |
| BigDecimal principalThresholdForLastInstallment = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(LoanProductConstants.principalThresholdForLastInstallmentParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.principalThresholdForLastInstallmentParamName) |
| .value(principalThresholdForLastInstallment).notLessThanMin(BigDecimal.ZERO).notGreaterThanMax(BigDecimal.valueOf(100)); |
| |
| BigDecimal fixedPrincipalPercentagePerInstallment = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName) |
| .value(fixedPrincipalPercentagePerInstallment).notLessThanMin(BigDecimal.ONE).notGreaterThanMax(BigDecimal.valueOf(100)); |
| |
| if (!amortizationType.equals(AmortizationMethod.EQUAL_PRINCIPAL.getValue()) && fixedPrincipalPercentagePerInstallment != null) { |
| baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName).failWithCode( |
| "not.supported.principal.fixing.not.allowed.with.equal.installments", |
| "Principal fixing cannot be done with equal installment amortization"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.canDefineEmiAmountParamName, element)) { |
| final Boolean canDefineInstallmentAmount = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.canDefineEmiAmountParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.canDefineEmiAmountParamName).value(canDefineInstallmentAmount) |
| .isOneOfTheseValues(true, false); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.installmentAmountInMultiplesOfParamName, element)) { |
| final Integer installmentAmountInMultiplesOf = this.fromApiJsonHelper |
| .extractIntegerWithLocaleNamed(LoanProductConstants.installmentAmountInMultiplesOfParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.installmentAmountInMultiplesOfParamName) |
| .value(installmentAmountInMultiplesOf).ignoreIfNull().integerGreaterThanZero(); |
| } |
| |
| // accounting related data validation |
| final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 4); |
| |
| if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType)) { |
| |
| final Long fundAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.FUND_SOURCE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.FUND_SOURCE.getValue()).value(fundAccountId).notNull() |
| .integerGreaterThanZero(); |
| |
| final Long loanPortfolioAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.LOAN_PORTFOLIO.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.LOAN_PORTFOLIO.getValue()).value(loanPortfolioAccountId) |
| .notNull().integerGreaterThanZero(); |
| |
| final Long transfersInSuspenseAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.TRANSFERS_SUSPENSE.getValue()) |
| .value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero(); |
| |
| final Long incomeFromInterestId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.INTEREST_ON_LOANS.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INTEREST_ON_LOANS.getValue()).value(incomeFromInterestId) |
| .notNull().integerGreaterThanZero(); |
| |
| final Long incomeFromFeeId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_FEES.getValue(), |
| element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId).notNull() |
| .integerGreaterThanZero(); |
| |
| final Long incomeFromPenaltyId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId) |
| .notNull().integerGreaterThanZero(); |
| |
| final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue()) |
| .value(incomeFromRecoveryAccountId).notNull().integerGreaterThanZero(); |
| |
| final Long writeOffAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId) |
| .notNull().integerGreaterThanZero(); |
| |
| final Long overpaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.OVERPAYMENT.getValue(), |
| element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.OVERPAYMENT.getValue()).value(overpaymentAccountId).notNull() |
| .integerGreaterThanZero(); |
| |
| validatePaymentChannelFundSourceMappings(baseDataValidator, element); |
| validateChargeToIncomeAccountMappings(baseDataValidator, element); |
| |
| } |
| |
| if (isAccrualBasedAccounting(accountingRuleType)) { |
| |
| final Long receivableInterestAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue()) |
| .value(receivableInterestAccountId).notNull().integerGreaterThanZero(); |
| |
| final Long receivableFeeAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.FEES_RECEIVABLE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.FEES_RECEIVABLE.getValue()).value(receivableFeeAccountId) |
| .notNull().integerGreaterThanZero(); |
| |
| final Long receivablePenaltyAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.PENALTIES_RECEIVABLE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.PENALTIES_RECEIVABLE.getValue()) |
| .value(receivablePenaltyAccountId).notNull().integerGreaterThanZero(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, element)) { |
| final Boolean useBorrowerCycle = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME).value(useBorrowerCycle) |
| .ignoreIfNull().validateForBooleanValue(); |
| if (useBorrowerCycle) { |
| validateBorrowerCycleVariations(element, baseDataValidator); |
| } |
| } |
| |
| validateMultiDisburseLoanData(baseDataValidator, element); |
| |
| validateLoanConfigurableAttributes(baseDataValidator, element); |
| |
| validateVariableInstallmentSettings(baseDataValidator, element); |
| |
| validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, null); |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.CAN_USE_FOR_TOPUP, element)) { |
| final Boolean canUseForTopup = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.CAN_USE_FOR_TOPUP, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.CAN_USE_FOR_TOPUP).value(canUseForTopup).validateForBooleanValue(); |
| } |
| |
| throwExceptionIfValidationWarningsExist(dataValidationErrors); |
| } |
| |
| private void validateVariableInstallmentSettings(final DataValidatorBuilder baseDataValidator, final JsonElement element) { |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.allowVariableInstallmentsParamName, element) |
| && this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.allowVariableInstallmentsParamName, element)) { |
| |
| boolean isEqualAmortization = false; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, element)) { |
| isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, element); |
| } |
| if (isEqualAmortization) { |
| throw new EqualAmortizationUnsupportedFeatureException("variable.installment", "variable installment"); |
| } |
| |
| Long minimumGapBetweenInstallments = null; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumGapBetweenInstallments, element)) { |
| minimumGapBetweenInstallments = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.minimumGapBetweenInstallments, |
| element); |
| baseDataValidator.reset().parameter(LoanProductConstants.minimumGapBetweenInstallments).value(minimumGapBetweenInstallments) |
| .notNull(); |
| } else { |
| baseDataValidator.reset().parameter(LoanProductConstants.minimumGapBetweenInstallments).failWithCode( |
| "is.mandatory.when.allowVariableInstallments.is.true", |
| "minimumGap param is mandatory when allowVariableInstallments is true"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.maximumGapBetweenInstallments, element)) { |
| final Long maximumGapBetweenInstallments = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductConstants.maximumGapBetweenInstallments, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.minimumGapBetweenInstallments).value(maximumGapBetweenInstallments) |
| .notNull(); |
| baseDataValidator.reset().parameter(LoanProductConstants.maximumGapBetweenInstallments).value(maximumGapBetweenInstallments) |
| .notNull().longGreaterThanNumber(minimumGapBetweenInstallments); |
| } |
| |
| } else { |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumGapBetweenInstallments, element)) { |
| baseDataValidator.reset().parameter(LoanProductConstants.minimumGapBetweenInstallments).failWithCode( |
| "not.supported.when.allowVariableInstallments.is.false", |
| "minimumGap param is not supported when allowVariableInstallments is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.maximumGapBetweenInstallments, element)) { |
| baseDataValidator.reset().parameter(LoanProductConstants.maximumGapBetweenInstallments).failWithCode( |
| "not.supported.when.allowVariableInstallments.is.false", |
| "maximumGap param is not supported when allowVariableInstallments is not supplied or false"); |
| } |
| |
| } |
| } |
| |
| private void validateLoanConfigurableAttributes(final DataValidatorBuilder baseDataValidator, final JsonElement element) { |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.allowAttributeOverridesParamName, element)) { |
| |
| final JsonObject object = element.getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName); |
| |
| // Validate that parameter names are allowed |
| Set<String> supportedConfigurableAttributes = new HashSet<>(); |
| Collections.addAll(supportedConfigurableAttributes, supportedloanConfigurableAttributes); |
| this.fromApiJsonHelper.checkForUnsupportedNestedParameters(LoanProductConstants.allowAttributeOverridesParamName, object, |
| supportedConfigurableAttributes); |
| |
| Integer length = supportedloanConfigurableAttributes.length; |
| |
| for (int i = 0; i < length; i++) { |
| /* Validate the attribute names */ |
| if (this.fromApiJsonHelper.parameterExists(supportedloanConfigurableAttributes[i], object)) { |
| Boolean loanConfigurationAttributeValue = this.fromApiJsonHelper |
| .extractBooleanNamed(supportedloanConfigurableAttributes[i], object); |
| /* Validate the boolean value */ |
| baseDataValidator.reset().parameter(LoanProductConstants.allowAttributeOverridesParamName) |
| .value(loanConfigurationAttributeValue).notNull().validateForBooleanValue(); |
| } |
| |
| } |
| } |
| } |
| |
| private void validateMultiDisburseLoanData(final DataValidatorBuilder baseDataValidator, final JsonElement element) { |
| Boolean multiDisburseLoan = false; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME, element)) { |
| multiDisburseLoan = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME, |
| element); |
| baseDataValidator.reset().parameter(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME).value(multiDisburseLoan) |
| .ignoreIfNull().validateForBooleanValue(); |
| } |
| |
| boolean isEqualAmortization = false; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, element)) { |
| isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, element); |
| } |
| if (isEqualAmortization && multiDisburseLoan) { |
| throw new EqualAmortizationUnsupportedFeatureException("tranche.disbursal", "tranche disbursal"); |
| } |
| |
| if (multiDisburseLoan) { |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.OUTSTANDING_LOAN_BALANCE_PARAMETER_NAME, element)) { |
| final BigDecimal outstandingLoanBalance = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(LoanProductConstants.OUTSTANDING_LOAN_BALANCE_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.OUTSTANDING_LOAN_BALANCE_PARAMETER_NAME) |
| .value(outstandingLoanBalance).ignoreIfNull().zeroOrPositiveAmount(); |
| } |
| |
| final Integer maxTrancheCount = this.fromApiJsonHelper |
| .extractIntegerNamed(LoanProductConstants.MAX_TRANCHE_COUNT_PARAMETER_NAME, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.MAX_TRANCHE_COUNT_PARAMETER_NAME).value(maxTrancheCount).notNull() |
| .integerGreaterThanZero(); |
| |
| final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed("interestType", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("interestType").value(interestType).ignoreIfNull() |
| .integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue()); |
| } |
| |
| final String overAppliedCalculationType = this.fromApiJsonHelper.extractStringNamed("overAppliedCalculationType", element); |
| baseDataValidator.reset().parameter("overAppliedCalculationType").value(overAppliedCalculationType).notExceedingLengthOf(10); |
| } |
| |
| private void validateInterestRecalculationParams(final JsonElement element, final DataValidatorBuilder baseDataValidator, |
| final LoanProduct loanProduct) { |
| |
| /** |
| * { @link InterestRecalculationCompoundingMethod } |
| */ |
| InterestRecalculationCompoundingMethod compoundingMethod = null; |
| |
| if (loanProduct == null || this.fromApiJsonHelper |
| .parameterExists(LoanProductConstants.interestRecalculationCompoundingMethodParameterName, element)) { |
| final Integer interestRecalculationCompoundingMethod = this.fromApiJsonHelper.extractIntegerNamed( |
| LoanProductConstants.interestRecalculationCompoundingMethodParameterName, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.interestRecalculationCompoundingMethodParameterName) |
| .value(interestRecalculationCompoundingMethod).notNull().inMinMaxRange(0, 3); |
| if (interestRecalculationCompoundingMethod != null) { |
| compoundingMethod = InterestRecalculationCompoundingMethod.fromInt(interestRecalculationCompoundingMethod); |
| } |
| } |
| |
| if (compoundingMethod == null) { |
| if (loanProduct == null) { |
| compoundingMethod = InterestRecalculationCompoundingMethod.NONE; |
| } else { |
| compoundingMethod = InterestRecalculationCompoundingMethod |
| .fromInt(loanProduct.getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod()); |
| } |
| } |
| |
| /** |
| * { @link LoanRescheduleStrategyMethod } |
| */ |
| if (loanProduct == null |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.rescheduleStrategyMethodParameterName, element)) { |
| final Integer rescheduleStrategyMethod = this.fromApiJsonHelper |
| .extractIntegerNamed(LoanProductConstants.rescheduleStrategyMethodParameterName, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.rescheduleStrategyMethodParameterName).value(rescheduleStrategyMethod) |
| .notNull().inMinMaxRange(1, 3); |
| } |
| |
| RecalculationFrequencyType frequencyType = null; |
| |
| if (loanProduct == null |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.recalculationRestFrequencyTypeParameterName, element)) { |
| final Integer recalculationRestFrequencyType = this.fromApiJsonHelper |
| .extractIntegerNamed(LoanProductConstants.recalculationRestFrequencyTypeParameterName, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.recalculationRestFrequencyTypeParameterName) |
| .value(recalculationRestFrequencyType).notNull().inMinMaxRange(1, 4); |
| if (recalculationRestFrequencyType != null) { |
| frequencyType = RecalculationFrequencyType.fromInt(recalculationRestFrequencyType); |
| } |
| } |
| |
| if (frequencyType == null) { |
| if (loanProduct == null) { |
| frequencyType = RecalculationFrequencyType.INVALID; |
| } else { |
| frequencyType = loanProduct.getProductInterestRecalculationDetails().getRestFrequencyType(); |
| } |
| } |
| |
| if (!frequencyType.isSameAsRepayment()) { |
| if (loanProduct == null || this.fromApiJsonHelper |
| .parameterExists(LoanProductConstants.recalculationRestFrequencyIntervalParameterName, element)) { |
| final Integer recurrenceInterval = this.fromApiJsonHelper.extractIntegerNamed( |
| LoanProductConstants.recalculationRestFrequencyIntervalParameterName, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.recalculationRestFrequencyIntervalParameterName) |
| .value(recurrenceInterval).notNull(); |
| } |
| if (loanProduct == null |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.recalculationRestFrequencyNthDayParamName, element) |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.recalculationRestFrequencyWeekdayParamName, element)) { |
| CalendarUtils.validateNthDayOfMonthFrequency(baseDataValidator, |
| LoanProductConstants.recalculationRestFrequencyNthDayParamName, |
| LoanProductConstants.recalculationRestFrequencyWeekdayParamName, element, this.fromApiJsonHelper); |
| } |
| if (loanProduct == null |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.recalculationRestFrequencyOnDayParamName, element)) { |
| final Integer recalculationRestFrequencyOnDay = this.fromApiJsonHelper |
| .extractIntegerNamed(LoanProductConstants.recalculationRestFrequencyOnDayParamName, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.recalculationRestFrequencyOnDayParamName) |
| .value(recalculationRestFrequencyOnDay).ignoreIfNull().inMinMaxRange(1, 28); |
| } |
| } |
| |
| if (compoundingMethod.isCompoundingEnabled()) { |
| RecalculationFrequencyType compoundingfrequencyType = null; |
| |
| if (loanProduct == null || this.fromApiJsonHelper |
| .parameterExists(LoanProductConstants.recalculationCompoundingFrequencyTypeParameterName, element)) { |
| final Integer recalculationCompoundingFrequencyType = this.fromApiJsonHelper.extractIntegerNamed( |
| LoanProductConstants.recalculationCompoundingFrequencyTypeParameterName, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.recalculationCompoundingFrequencyTypeParameterName) |
| .value(recalculationCompoundingFrequencyType).notNull().inMinMaxRange(1, 4); |
| if (recalculationCompoundingFrequencyType != null) { |
| compoundingfrequencyType = RecalculationFrequencyType.fromInt(recalculationCompoundingFrequencyType); |
| if (!compoundingfrequencyType.isSameAsRepayment()) { |
| PeriodFrequencyType repaymentFrequencyType = null; |
| if (this.fromApiJsonHelper.parameterExists("repaymentFrequencyType", element)) { |
| Integer repaymentFrequencyTypeVal = this.fromApiJsonHelper.extractIntegerNamed("repaymentFrequencyType", |
| element, Locale.getDefault()); |
| repaymentFrequencyType = PeriodFrequencyType.fromInt(repaymentFrequencyTypeVal); |
| } else if (loanProduct != null) { |
| repaymentFrequencyType = loanProduct.getLoanProductRelatedDetail().getRepaymentPeriodFrequencyType(); |
| } |
| if (!compoundingfrequencyType.isSameFrequency(repaymentFrequencyType)) { |
| baseDataValidator.reset().parameter(LoanProductConstants.recalculationCompoundingFrequencyTypeParameterName) |
| .value(recalculationCompoundingFrequencyType).failWithCode("must.be.same.as.repayment.frequency"); |
| } |
| } |
| } |
| } |
| |
| if (compoundingfrequencyType == null) { |
| if (loanProduct == null) { |
| compoundingfrequencyType = RecalculationFrequencyType.INVALID; |
| } else { |
| compoundingfrequencyType = loanProduct.getProductInterestRecalculationDetails().getCompoundingFrequencyType(); |
| } |
| } |
| |
| if (!compoundingfrequencyType.isSameAsRepayment()) { |
| if (loanProduct == null || this.fromApiJsonHelper |
| .parameterExists(LoanProductConstants.recalculationCompoundingFrequencyIntervalParameterName, element)) { |
| final Integer recurrenceInterval = this.fromApiJsonHelper.extractIntegerNamed( |
| LoanProductConstants.recalculationCompoundingFrequencyIntervalParameterName, element, Locale.getDefault()); |
| Integer repaymentEvery = null; |
| if (loanProduct == null) { |
| repaymentEvery = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element); |
| } else { |
| repaymentEvery = loanProduct.getLoanProductRelatedDetail().getRepayEvery(); |
| } |
| |
| baseDataValidator.reset().parameter(LoanProductConstants.recalculationCompoundingFrequencyIntervalParameterName) |
| .value(recurrenceInterval).notNull().integerInMultiplesOfNumber(repaymentEvery); |
| } |
| if (loanProduct == null |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.recalculationCompoundingFrequencyNthDayParamName, |
| element) |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.recalculationCompoundingFrequencyWeekdayParamName, |
| element)) { |
| CalendarUtils.validateNthDayOfMonthFrequency(baseDataValidator, |
| LoanProductConstants.recalculationCompoundingFrequencyNthDayParamName, |
| LoanProductConstants.recalculationCompoundingFrequencyWeekdayParamName, element, this.fromApiJsonHelper); |
| } |
| if (loanProduct == null || this.fromApiJsonHelper |
| .parameterExists(LoanProductConstants.recalculationCompoundingFrequencyOnDayParamName, element)) { |
| final Integer recalculationRestFrequencyOnDay = this.fromApiJsonHelper.extractIntegerNamed( |
| LoanProductConstants.recalculationCompoundingFrequencyOnDayParamName, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.recalculationCompoundingFrequencyOnDayParamName) |
| .value(recalculationRestFrequencyOnDay).ignoreIfNull().inMinMaxRange(1, 28); |
| } |
| } |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName, element)) { |
| final Boolean isArrearsBasedOnOriginalSchedule = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName) |
| .value(isArrearsBasedOnOriginalSchedule).notNull().isOneOfTheseValues(true, false); |
| } |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, element)) { |
| final Boolean isCompoundingToBePostedAsTransactions = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.isCompoundingToBePostedAsTransactionParamName) |
| .value(isCompoundingToBePostedAsTransactions).notNull().isOneOfTheseValues(true, false); |
| } |
| |
| final Integer preCloseInterestCalculationStrategy = this.fromApiJsonHelper |
| .extractIntegerWithLocaleNamed(LoanProductConstants.preClosureInterestCalculationStrategyParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.preClosureInterestCalculationStrategyParamName) |
| .value(preCloseInterestCalculationStrategy).ignoreIfNull().inMinMaxRange( |
| LoanPreClosureInterestCalculationStrategy.getMinValue(), LoanPreClosureInterestCalculationStrategy.getMaxValue()); |
| } |
| |
| public void validateForUpdate(final String json, final LoanProduct loanProduct) { |
| if (StringUtils.isBlank(json)) { |
| throw new InvalidJsonException(); |
| } |
| |
| final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType(); |
| this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); |
| |
| final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); |
| final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanproduct"); |
| |
| final JsonElement element = this.fromApiJsonHelper.parse(json); |
| if (this.fromApiJsonHelper.parameterExists("name", element)) { |
| final String name = this.fromApiJsonHelper.extractStringNamed("name", element); |
| baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.SHORT_NAME, element)) { |
| final String shortName = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.SHORT_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.SHORT_NAME).value(shortName).notBlank().notExceedingLengthOf(4); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("description", element)) { |
| final String description = this.fromApiJsonHelper.extractStringNamed("description", element); |
| baseDataValidator.reset().parameter("description").value(description).notExceedingLengthOf(500); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("fundId", element)) { |
| final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element); |
| baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("includeInBorrowerCycle", element)) { |
| final Boolean includeInBorrowerCycle = this.fromApiJsonHelper.extractBooleanNamed("includeInBorrowerCycle", element); |
| baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull() |
| .validateForBooleanValue(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("currencyCode", element)) { |
| final String currencyCode = this.fromApiJsonHelper.extractStringNamed("currencyCode", element); |
| baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("digitsAfterDecimal", element)) { |
| final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, |
| Locale.getDefault()); |
| baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("inMultiplesOf", element)) { |
| final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerNamed("inMultiplesOf", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("inMultiplesOf").value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater(); |
| } |
| |
| final String minPrincipalParameterName = "minPrincipal"; |
| BigDecimal minPrincipalAmount = null; |
| if (this.fromApiJsonHelper.parameterExists(minPrincipalParameterName, element)) { |
| minPrincipalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minPrincipalParameterName, element); |
| baseDataValidator.reset().parameter(minPrincipalParameterName).value(minPrincipalAmount).ignoreIfNull().positiveAmount(); |
| } |
| |
| final String maxPrincipalParameterName = "maxPrincipal"; |
| BigDecimal maxPrincipalAmount = null; |
| if (this.fromApiJsonHelper.parameterExists(maxPrincipalParameterName, element)) { |
| maxPrincipalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxPrincipalParameterName, element); |
| baseDataValidator.reset().parameter(maxPrincipalParameterName).value(maxPrincipalAmount).ignoreIfNull().positiveAmount(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("principal", element)) { |
| final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element); |
| baseDataValidator.reset().parameter("principal").value(principal).positiveAmount(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("inArrearsTolerance", element)) { |
| final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element); |
| baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount(); |
| } |
| |
| final String minNumberOfRepaymentsParameterName = "minNumberOfRepayments"; |
| Integer minNumberOfRepayments = null; |
| if (this.fromApiJsonHelper.parameterExists(minNumberOfRepaymentsParameterName, element)) { |
| minNumberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(minNumberOfRepaymentsParameterName, element); |
| baseDataValidator.reset().parameter(minNumberOfRepaymentsParameterName).value(minNumberOfRepayments).ignoreIfNull() |
| .integerGreaterThanZero(); |
| } |
| |
| final String maxNumberOfRepaymentsParameterName = "maxNumberOfRepayments"; |
| Integer maxNumberOfRepayments = null; |
| if (this.fromApiJsonHelper.parameterExists(maxNumberOfRepaymentsParameterName, element)) { |
| maxNumberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(maxNumberOfRepaymentsParameterName, element); |
| baseDataValidator.reset().parameter(maxNumberOfRepaymentsParameterName).value(maxNumberOfRepayments).ignoreIfNull() |
| .integerGreaterThanZero(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("numberOfRepayments", element)) { |
| final Integer numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element); |
| baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("repaymentEvery", element)) { |
| final Integer repaymentEvery = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element); |
| baseDataValidator.reset().parameter("repaymentEvery").value(repaymentEvery).notNull().integerGreaterThanZero(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("repaymentFrequencyType", element)) { |
| final Integer repaymentFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("repaymentFrequencyType", element, |
| Locale.getDefault()); |
| baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("transactionProcessingStrategyId", element)) { |
| final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", |
| element); |
| baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull() |
| .integerGreaterThanZero(); |
| } |
| |
| // grace validation |
| if (this.fromApiJsonHelper.parameterExists("graceOnPrincipalPayment", element)) { |
| final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", |
| element); |
| baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(graceOnPrincipalPayment).zeroOrPositiveAmount(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("graceOnInterestPayment", element)) { |
| final Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element); |
| baseDataValidator.reset().parameter("graceOnInterestPayment").value(graceOnInterestPayment).zeroOrPositiveAmount(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("graceOnInterestCharged", element)) { |
| final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element); |
| baseDataValidator.reset().parameter("graceOnInterestCharged").value(graceOnInterestCharged).zeroOrPositiveAmount(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, element)) { |
| final Integer graceOnArrearsAgeing = this.fromApiJsonHelper |
| .extractIntegerWithLocaleNamed(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME).value(graceOnArrearsAgeing) |
| .integerZeroOrGreater(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.OVERDUE_DAYS_FOR_NPA_PARAMETER_NAME, element)) { |
| final Integer overdueDaysForNPA = this.fromApiJsonHelper |
| .extractIntegerWithLocaleNamed(LoanProductConstants.OVERDUE_DAYS_FOR_NPA_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.OVERDUE_DAYS_FOR_NPA_PARAMETER_NAME).value(overdueDaysForNPA) |
| .integerZeroOrGreater(); |
| } |
| |
| Integer amortizationType = null; |
| if (this.fromApiJsonHelper.parameterExists("amortizationType", element)) { |
| amortizationType = this.fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("interestType", element)) { |
| final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed("interestType", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("interestType").value(interestType).notNull().inMinMaxRange(0, 1); |
| } |
| Integer interestCalculationPeriodType = loanProduct.getLoanProductRelatedDetail().getInterestCalculationPeriodMethod().getValue(); |
| if (this.fromApiJsonHelper.parameterExists("interestCalculationPeriodType", element)) { |
| interestCalculationPeriodType = this.fromApiJsonHelper.extractIntegerNamed("interestCalculationPeriodType", element, |
| Locale.getDefault()); |
| baseDataValidator.reset().parameter("interestCalculationPeriodType").value(interestCalculationPeriodType).notNull() |
| .inMinMaxRange(0, 1); |
| } |
| |
| /** |
| * { @link DaysInYearType } |
| */ |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME, element)) { |
| final Integer daysInYearType = this.fromApiJsonHelper.extractIntegerNamed(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME, |
| element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME).value(daysInYearType).notNull() |
| .isOneOfTheseValues(1, 360, 364, 365); |
| } |
| |
| /** |
| * { @link DaysInMonthType } |
| */ |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME, element)) { |
| final Integer daysInMonthType = this.fromApiJsonHelper |
| .extractIntegerNamed(LoanProductConstants.DAYS_IN_MONTH_TYPE_PARAMETER_NAME, element, Locale.getDefault()); |
| baseDataValidator.reset().parameter(LoanProductConstants.DAYS_IN_MONTH_TYPE_PARAMETER_NAME).value(daysInMonthType).notNull() |
| .isOneOfTheseValues(1, 30); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ACCOUNT_MOVES_OUT_OF_NPA_ONLY_ON_ARREARS_COMPLETION_PARAM_NAME, |
| element)) { |
| Boolean npaChangeConfig = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.ACCOUNT_MOVES_OUT_OF_NPA_ONLY_ON_ARREARS_COMPLETION_PARAM_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.ACCOUNT_MOVES_OUT_OF_NPA_ONLY_ON_ARREARS_COMPLETION_PARAM_NAME) |
| .value(npaChangeConfig).notNull().isOneOfTheseValues(true, false); |
| } |
| |
| boolean isEqualAmortization = loanProduct.isEqualAmortization(); |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, element)) { |
| isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM).value(isEqualAmortization).ignoreIfNull() |
| .validateForBooleanValue(); |
| } |
| |
| // Interest recalculation settings |
| Boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled(); |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME, element)) { |
| isInterestRecalculationEnabled = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME) |
| .value(isInterestRecalculationEnabled).notNull().isOneOfTheseValues(true, false); |
| } |
| |
| if (isInterestRecalculationEnabled != null) { |
| if (isInterestRecalculationEnabled) { |
| if (isEqualAmortization) { |
| throw new EqualAmortizationUnsupportedFeatureException("interest.recalculation", "interest recalculation"); |
| } |
| validateInterestRecalculationParams(element, baseDataValidator, loanProduct); |
| } |
| } |
| |
| // interest rates |
| boolean isLinkedToFloatingInterestRates = loanProduct.isLinkedToFloatingInterestRate(); |
| if (this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)) { |
| isLinkedToFloatingInterestRates = this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element); |
| } |
| if (isLinkedToFloatingInterestRates) { |
| if (isEqualAmortization) { |
| throw new EqualAmortizationUnsupportedFeatureException("floating.interest.rate", "floating interest rate"); |
| } |
| if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) { |
| baseDataValidator.reset().parameter("interestRatePerPeriod").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.true", |
| "interestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("minInterestRatePerPeriod", element)) { |
| baseDataValidator.reset().parameter("minInterestRatePerPeriod").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.true", |
| "minInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("maxInterestRatePerPeriod", element)) { |
| baseDataValidator.reset().parameter("maxInterestRatePerPeriod").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.true", |
| "maxInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) { |
| baseDataValidator.reset().parameter("interestRateFrequencyType").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.true", |
| "interestRateFrequencyType param is not supported when isLinkedToFloatingInterestRates is true"); |
| } |
| |
| Integer interestType = this.fromApiJsonHelper.parameterExists("interestType", element) |
| ? this.fromApiJsonHelper.extractIntegerNamed("interestType", element, Locale.getDefault()) |
| : loanProduct.getLoanProductRelatedDetail().getInterestMethod().getValue(); |
| if ((interestType == null || !interestType.equals(InterestMethod.DECLINING_BALANCE.getValue())) |
| || (isInterestRecalculationEnabled == null || isInterestRecalculationEnabled == false)) { |
| baseDataValidator.reset().parameter("isLinkedToFloatingInterestRates").failWithCode( |
| "supported.only.for.declining.balance.interest.recalculation.enabled", |
| "Floating interest rates are supported only for declining balance and interest recalculation enabled loan products"); |
| } |
| |
| Long floatingRatesId = loanProduct.getFloatingRates() == null ? null : loanProduct.getFloatingRates().getFloatingRate().getId(); |
| if (this.fromApiJsonHelper.parameterExists("floatingRatesId", element)) { |
| floatingRatesId = this.fromApiJsonHelper.extractLongNamed("floatingRatesId", element); |
| } |
| baseDataValidator.reset().parameter("floatingRatesId").value(floatingRatesId).notNull(); |
| |
| BigDecimal interestRateDifferential = loanProduct.getFloatingRates() == null ? null |
| : loanProduct.getFloatingRates().getInterestRateDifferential(); |
| if (this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)) { |
| interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRateDifferential", element); |
| } |
| baseDataValidator.reset().parameter("interestRateDifferential").value(interestRateDifferential).notNull() |
| .zeroOrPositiveAmount(); |
| |
| final String minDifferentialLendingRateParameterName = "minDifferentialLendingRate"; |
| BigDecimal minDifferentialLendingRate = loanProduct.getFloatingRates() == null ? null |
| : loanProduct.getFloatingRates().getMinDifferentialLendingRate(); |
| if (this.fromApiJsonHelper.parameterExists(minDifferentialLendingRateParameterName, element)) { |
| minDifferentialLendingRate = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(minDifferentialLendingRateParameterName, element); |
| } |
| baseDataValidator.reset().parameter(minDifferentialLendingRateParameterName).value(minDifferentialLendingRate).notNull() |
| .zeroOrPositiveAmount(); |
| |
| final String defaultDifferentialLendingRateParameterName = "defaultDifferentialLendingRate"; |
| BigDecimal defaultDifferentialLendingRate = loanProduct.getFloatingRates() == null ? null |
| : loanProduct.getFloatingRates().getDefaultDifferentialLendingRate(); |
| if (this.fromApiJsonHelper.parameterExists(defaultDifferentialLendingRateParameterName, element)) { |
| defaultDifferentialLendingRate = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(defaultDifferentialLendingRateParameterName, element); |
| } |
| baseDataValidator.reset().parameter(defaultDifferentialLendingRateParameterName).value(defaultDifferentialLendingRate).notNull() |
| .zeroOrPositiveAmount(); |
| |
| final String maxDifferentialLendingRateParameterName = "maxDifferentialLendingRate"; |
| BigDecimal maxDifferentialLendingRate = loanProduct.getFloatingRates() == null ? null |
| : loanProduct.getFloatingRates().getMaxDifferentialLendingRate(); |
| if (this.fromApiJsonHelper.parameterExists(maxDifferentialLendingRateParameterName, element)) { |
| maxDifferentialLendingRate = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(maxDifferentialLendingRateParameterName, element); |
| } |
| baseDataValidator.reset().parameter(maxDifferentialLendingRateParameterName).value(maxDifferentialLendingRate).notNull() |
| .zeroOrPositiveAmount(); |
| |
| if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| baseDataValidator.reset().parameter("defaultDifferentialLendingRate").value(defaultDifferentialLendingRate) |
| .notLessThanMin(minDifferentialLendingRate); |
| } |
| } |
| |
| if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate) |
| .notLessThanMin(minDifferentialLendingRate); |
| } |
| } |
| |
| if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) >= 0) { |
| baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate) |
| .notLessThanMin(defaultDifferentialLendingRate); |
| } |
| } |
| |
| Boolean isFloatingInterestRateCalculationAllowed = loanProduct.getFloatingRates() == null ? null |
| : loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed(); |
| if (this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)) { |
| isFloatingInterestRateCalculationAllowed = this.fromApiJsonHelper |
| .extractBooleanNamed("isFloatingInterestRateCalculationAllowed", element); |
| } |
| baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").value(isFloatingInterestRateCalculationAllowed) |
| .notNull().isOneOfTheseValues(true, false); |
| } else { |
| if (this.fromApiJsonHelper.parameterExists("floatingRatesId", element)) { |
| baseDataValidator.reset().parameter("floatingRatesId").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "floatingRatesId param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)) { |
| baseDataValidator.reset().parameter("interestRateDifferential").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "interestRateDifferential param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("minDifferentialLendingRate", element)) { |
| baseDataValidator.reset().parameter("minDifferentialLendingRate").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "minDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("defaultDifferentialLendingRate", element)) { |
| baseDataValidator.reset().parameter("defaultDifferentialLendingRate").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "defaultDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("maxDifferentialLendingRate", element)) { |
| baseDataValidator.reset().parameter("maxDifferentialLendingRate").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "maxDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)) { |
| baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").failWithCode( |
| "not.supported.when.isLinkedToFloatingInterestRates.is.false", |
| "isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false"); |
| } |
| |
| final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod"; |
| BigDecimal minInterestRatePerPeriod = loanProduct.getMinNominalInterestRatePerPeriod(); |
| if (this.fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) { |
| minInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName, |
| element); |
| } |
| baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull() |
| .zeroOrPositiveAmount(); |
| |
| final String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod"; |
| BigDecimal maxInterestRatePerPeriod = loanProduct.getMaxNominalInterestRatePerPeriod(); |
| if (this.fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) { |
| maxInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName, |
| element); |
| } |
| baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull() |
| .zeroOrPositiveAmount(); |
| |
| BigDecimal interestRatePerPeriod = loanProduct.getLoanProductRelatedDetail().getNominalInterestRatePerPeriod(); |
| if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) { |
| interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element); |
| } |
| baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount(); |
| |
| Integer interestRateFrequencyType = loanProduct.getLoanProductRelatedDetail().getInterestPeriodFrequencyType().getValue(); |
| if (this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) { |
| interestRateFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element, |
| Locale.getDefault()); |
| } |
| baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 4); |
| } |
| |
| // Guarantee Funds |
| Boolean holdGuaranteeFunds = loanProduct.isHoldGuaranteeFundsEnabled(); |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.holdGuaranteeFundsParamName, element)) { |
| holdGuaranteeFunds = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.holdGuaranteeFundsParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.holdGuaranteeFundsParamName).value(holdGuaranteeFunds).notNull() |
| .isOneOfTheseValues(true, false); |
| } |
| |
| if (holdGuaranteeFunds != null) { |
| if (holdGuaranteeFunds) { |
| validateGuaranteeParams(element, baseDataValidator, null); |
| } |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.principalThresholdForLastInstallmentParamName, element)) { |
| BigDecimal principalThresholdForLastInstallment = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(LoanProductConstants.principalThresholdForLastInstallmentParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.principalThresholdForLastInstallmentParamName) |
| .value(principalThresholdForLastInstallment).notNull().notLessThanMin(BigDecimal.ZERO) |
| .notGreaterThanMax(BigDecimal.valueOf(100)); |
| } |
| |
| BigDecimal fixedPrincipalPercentagePerInstallment = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName) |
| .value(fixedPrincipalPercentagePerInstallment).notLessThanMin(BigDecimal.ONE).notGreaterThanMax(BigDecimal.valueOf(100)); |
| |
| if (!AmortizationMethod.EQUAL_PRINCIPAL.getValue().equals(amortizationType) && fixedPrincipalPercentagePerInstallment != null) { |
| baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName).failWithCode( |
| "not.supported.principal.fixing.not.allowed.with.equal.installments", |
| "Principal fixing cannot be done with equal installment amortization"); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.canDefineEmiAmountParamName, element)) { |
| final Boolean canDefineInstallmentAmount = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.canDefineEmiAmountParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.canDefineEmiAmountParamName).value(canDefineInstallmentAmount) |
| .isOneOfTheseValues(true, false); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.installmentAmountInMultiplesOfParamName, element)) { |
| final Integer installmentAmountInMultiplesOf = this.fromApiJsonHelper |
| .extractIntegerWithLocaleNamed(LoanProductConstants.installmentAmountInMultiplesOfParamName, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.installmentAmountInMultiplesOfParamName) |
| .value(installmentAmountInMultiplesOf).ignoreIfNull().integerGreaterThanZero(); |
| } |
| |
| final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault()); |
| baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).ignoreIfNull().inMinMaxRange(1, 4); |
| |
| final Long fundAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.FUND_SOURCE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.FUND_SOURCE.getValue()).value(fundAccountId).ignoreIfNull() |
| .integerGreaterThanZero(); |
| |
| final Long loanPortfolioAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.LOAN_PORTFOLIO.getValue(), |
| element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.LOAN_PORTFOLIO.getValue()).value(loanPortfolioAccountId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| final Long transfersInSuspenseAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.TRANSFERS_SUSPENSE.getValue()).value(transfersInSuspenseAccountId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| final Long incomeFromInterestId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.INTEREST_ON_LOANS.getValue(), |
| element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INTEREST_ON_LOANS.getValue()).value(incomeFromInterestId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| final Long incomeFromFeeId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_FEES.getValue(), |
| element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId).ignoreIfNull() |
| .integerGreaterThanZero(); |
| |
| final Long incomeFromPenaltyId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue()).value(incomeFromRecoveryAccountId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), |
| element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| final Long overpaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.OVERPAYMENT.getValue(), |
| element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.OVERPAYMENT.getValue()).value(overpaymentAccountId).ignoreIfNull() |
| .integerGreaterThanZero(); |
| |
| final Long receivableInterestAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue()).value(receivableInterestAccountId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| final Long receivableFeeAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.FEES_RECEIVABLE.getValue(), |
| element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.FEES_RECEIVABLE.getValue()).value(receivableFeeAccountId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| final Long receivablePenaltyAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.PENALTIES_RECEIVABLE.getValue(), element); |
| baseDataValidator.reset().parameter(LoanProductAccountingParams.PENALTIES_RECEIVABLE.getValue()).value(receivablePenaltyAccountId) |
| .ignoreIfNull().integerGreaterThanZero(); |
| |
| validatePaymentChannelFundSourceMappings(baseDataValidator, element); |
| validateChargeToIncomeAccountMappings(baseDataValidator, element); |
| |
| validateMinMaxConstraints(element, baseDataValidator, loanProduct); |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, element)) { |
| final Boolean useBorrowerCycle = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME).value(useBorrowerCycle) |
| .ignoreIfNull().validateForBooleanValue(); |
| if (useBorrowerCycle) { |
| validateBorrowerCycleVariations(element, baseDataValidator); |
| } |
| } |
| |
| validateMultiDisburseLoanData(baseDataValidator, element); |
| |
| // validateLoanConfigurableAttributes(baseDataValidator,element); |
| |
| validateVariableInstallmentSettings(baseDataValidator, element); |
| |
| validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct); |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.CAN_USE_FOR_TOPUP, element)) { |
| final Boolean canUseForTopup = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.CAN_USE_FOR_TOPUP, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.CAN_USE_FOR_TOPUP).value(canUseForTopup).validateForBooleanValue(); |
| } |
| |
| throwExceptionIfValidationWarningsExist(dataValidationErrors); |
| } |
| |
| /* |
| * Validation for advanced accounting options |
| */ |
| private void validatePaymentChannelFundSourceMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) { |
| if (this.fromApiJsonHelper.parameterExists(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element)) { |
| final JsonArray paymentChannelMappingArray = this.fromApiJsonHelper |
| .extractJsonArrayNamed(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element); |
| if (paymentChannelMappingArray != null && paymentChannelMappingArray.size() > 0) { |
| int i = 0; |
| do { |
| final JsonObject jsonObject = paymentChannelMappingArray.get(i).getAsJsonObject(); |
| final Long paymentTypeId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.PAYMENT_TYPE.getValue(), |
| jsonObject); |
| final Long paymentSpecificFundAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.FUND_SOURCE.getValue(), jsonObject); |
| |
| baseDataValidator.reset() |
| .parameter(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue() + "[" + i + "]." |
| + LoanProductAccountingParams.PAYMENT_TYPE.getValue()) |
| .value(paymentTypeId).notNull().integerGreaterThanZero(); |
| baseDataValidator.reset() |
| .parameter(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue() + "[" + i + "]." |
| + LoanProductAccountingParams.FUND_SOURCE.getValue()) |
| .value(paymentSpecificFundAccountId).notNull().integerGreaterThanZero(); |
| i++; |
| } while (i < paymentChannelMappingArray.size()); |
| } |
| } |
| } |
| |
| private void validateChargeToIncomeAccountMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) { |
| // validate for both fee and penalty charges |
| validateChargeToIncomeAccountMappings(baseDataValidator, element, true); |
| validateChargeToIncomeAccountMappings(baseDataValidator, element, true); |
| } |
| |
| private void validateChargeToIncomeAccountMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element, |
| final boolean isPenalty) { |
| String parameterName; |
| if (isPenalty) { |
| parameterName = LoanProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(); |
| } else { |
| parameterName = LoanProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue(); |
| } |
| |
| if (this.fromApiJsonHelper.parameterExists(parameterName, element)) { |
| final JsonArray chargeToIncomeAccountMappingArray = this.fromApiJsonHelper.extractJsonArrayNamed(parameterName, element); |
| if (chargeToIncomeAccountMappingArray != null && chargeToIncomeAccountMappingArray.size() > 0) { |
| int i = 0; |
| do { |
| final JsonObject jsonObject = chargeToIncomeAccountMappingArray.get(i).getAsJsonObject(); |
| final Long chargeId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.CHARGE_ID.getValue(), |
| jsonObject); |
| final Long incomeAccountId = this.fromApiJsonHelper |
| .extractLongNamed(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue(), jsonObject); |
| baseDataValidator.reset().parameter(parameterName + "[" + i + "]." + LoanProductAccountingParams.CHARGE_ID.getValue()) |
| .value(chargeId).notNull().integerGreaterThanZero(); |
| baseDataValidator.reset() |
| .parameter(parameterName + "[" + i + "]." + LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue()) |
| .value(incomeAccountId).notNull().integerGreaterThanZero(); |
| i++; |
| } while (i < chargeToIncomeAccountMappingArray.size()); |
| } |
| } |
| } |
| |
| public void validateMinMaxConstraints(final JsonElement element, final DataValidatorBuilder baseDataValidator, |
| final LoanProduct loanProduct) { |
| |
| validatePrincipalMinMaxConstraint(element, loanProduct, baseDataValidator); |
| |
| validateNumberOfRepaymentsMinMaxConstraint(element, loanProduct, baseDataValidator); |
| |
| validateNominalInterestRatePerPeriodMinMaxConstraint(element, loanProduct, baseDataValidator); |
| } |
| |
| public void validateMinMaxConstraints(final JsonElement element, final DataValidatorBuilder baseDataValidator, |
| final LoanProduct loanProduct, Integer cycleNumber) { |
| |
| final Map<String, BigDecimal> minmaxValues = loanProduct.fetchBorrowerCycleVariationsForCycleNumber(cycleNumber); |
| final String principalParameterName = "principal"; |
| BigDecimal principalAmount = null; |
| BigDecimal minPrincipalAmount = null; |
| BigDecimal maxPrincipalAmount = null; |
| if (this.fromApiJsonHelper.parameterExists(principalParameterName, element)) { |
| principalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(principalParameterName, element); |
| minPrincipalAmount = minmaxValues.get(LoanProductConstants.MIN_PRINCIPAL); |
| maxPrincipalAmount = minmaxValues.get(LoanProductConstants.MAX_PRINCIPAL); |
| } |
| |
| if ((minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) > 0) |
| && (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) > 0)) { |
| baseDataValidator.reset().parameter(principalParameterName).value(principalAmount).inMinAndMaxAmountRange(minPrincipalAmount, |
| maxPrincipalAmount); |
| } else { |
| if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) > 0) { |
| baseDataValidator.reset().parameter(principalParameterName).value(principalAmount).notLessThanMin(minPrincipalAmount); |
| } else if (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) > 0) { |
| baseDataValidator.reset().parameter(principalParameterName).value(principalAmount).notGreaterThanMax(maxPrincipalAmount); |
| } |
| } |
| |
| final String numberOfRepaymentsParameterName = "numberOfRepayments"; |
| Integer maxNumberOfRepayments = null; |
| Integer minNumberOfRepayments = null; |
| Integer numberOfRepayments = null; |
| if (this.fromApiJsonHelper.parameterExists(numberOfRepaymentsParameterName, element)) { |
| numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(numberOfRepaymentsParameterName, element); |
| if (minmaxValues.get(LoanProductConstants.MIN_NUMBER_OF_REPAYMENTS) != null) { |
| minNumberOfRepayments = minmaxValues.get(LoanProductConstants.MIN_NUMBER_OF_REPAYMENTS).intValueExact(); |
| } |
| if (minmaxValues.get(LoanProductConstants.MAX_NUMBER_OF_REPAYMENTS) != null) { |
| maxNumberOfRepayments = minmaxValues.get(LoanProductConstants.MAX_NUMBER_OF_REPAYMENTS).intValueExact(); |
| } |
| } |
| |
| if (maxNumberOfRepayments != null && maxNumberOfRepayments.compareTo(0) > 0) { |
| if (minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) > 0) { |
| 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) > 0) { |
| baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments) |
| .notLessThanMin(minNumberOfRepayments); |
| } |
| |
| final String interestRatePerPeriodParameterName = "interestRatePerPeriod"; |
| BigDecimal interestRatePerPeriod = null; |
| BigDecimal minInterestRatePerPeriod = null; |
| BigDecimal maxInterestRatePerPeriod = null; |
| if (this.fromApiJsonHelper.parameterExists(interestRatePerPeriodParameterName, element)) { |
| interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(interestRatePerPeriodParameterName, element); |
| minInterestRatePerPeriod = minmaxValues.get(LoanProductConstants.MIN_INTEREST_RATE_PER_PERIOD); |
| maxInterestRatePerPeriod = minmaxValues.get(LoanProductConstants.MAX_INTEREST_RATE_PER_PERIOD); |
| } |
| 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 void validatePrincipalMinMaxConstraint(final JsonElement element, final LoanProduct loanProduct, |
| final DataValidatorBuilder baseDataValidator) { |
| |
| boolean principalUpdated = false; |
| boolean minPrincipalUpdated = false; |
| boolean maxPrincipalUpdated = false; |
| final String principalParameterName = "principal"; |
| BigDecimal principalAmount = null; |
| if (this.fromApiJsonHelper.parameterExists(principalParameterName, element)) { |
| principalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(principalParameterName, element); |
| principalUpdated = true; |
| } else { |
| principalAmount = loanProduct.getPrincipalAmount().getAmount(); |
| } |
| |
| final String minPrincipalParameterName = "minPrincipal"; |
| BigDecimal minPrincipalAmount = null; |
| if (this.fromApiJsonHelper.parameterExists(minPrincipalParameterName, element)) { |
| minPrincipalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minPrincipalParameterName, element); |
| minPrincipalUpdated = true; |
| } else { |
| minPrincipalAmount = loanProduct.getMinPrincipalAmount().getAmount(); |
| } |
| |
| final String maxPrincipalParameterName = "maxPrincipal"; |
| BigDecimal maxPrincipalAmount = null; |
| if (this.fromApiJsonHelper.parameterExists(maxPrincipalParameterName, element)) { |
| maxPrincipalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxPrincipalParameterName, element); |
| maxPrincipalUpdated = true; |
| } else { |
| maxPrincipalAmount = loanProduct.getMaxPrincipalAmount().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) > 0) |
| && (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) > 0)) { |
| baseDataValidator.reset().parameter(principalParameterName).value(principalAmount) |
| .inMinAndMaxAmountRange(minPrincipalAmount, maxPrincipalAmount); |
| } else { |
| if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) > 0) { |
| baseDataValidator.reset().parameter(principalParameterName).value(principalAmount).notLessThanMin(minPrincipalAmount); |
| } else if (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) > 0) { |
| baseDataValidator.reset().parameter(principalParameterName).value(principalAmount) |
| .notGreaterThanMax(maxPrincipalAmount); |
| } |
| } |
| } |
| } |
| |
| private void validateNumberOfRepaymentsMinMaxConstraint(final JsonElement element, final LoanProduct loanProduct, |
| final DataValidatorBuilder baseDataValidator) { |
| boolean numberOfRepaymentsUpdated = false; |
| boolean minNumberOfRepaymentsUpdated = false; |
| boolean maxNumberOfRepaymentsUpdated = false; |
| |
| final String numberOfRepaymentsParameterName = "numberOfRepayments"; |
| Integer numberOfRepayments = null; |
| if (this.fromApiJsonHelper.parameterExists(numberOfRepaymentsParameterName, element)) { |
| numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(numberOfRepaymentsParameterName, element); |
| numberOfRepaymentsUpdated = true; |
| } else { |
| numberOfRepayments = loanProduct.getNumberOfRepayments(); |
| } |
| |
| final String minNumberOfRepaymentsParameterName = "minNumberOfRepayments"; |
| Integer minNumberOfRepayments = null; |
| if (this.fromApiJsonHelper.parameterExists(minNumberOfRepaymentsParameterName, element)) { |
| minNumberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(minNumberOfRepaymentsParameterName, element); |
| minNumberOfRepaymentsUpdated = true; |
| } else { |
| minNumberOfRepayments = loanProduct.getMinNumberOfRepayments(); |
| } |
| |
| final String maxNumberOfRepaymentsParameterName = "maxNumberOfRepayments"; |
| Integer maxNumberOfRepayments = null; |
| if (this.fromApiJsonHelper.parameterExists(maxNumberOfRepaymentsParameterName, element)) { |
| maxNumberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(maxNumberOfRepaymentsParameterName, element); |
| maxNumberOfRepaymentsUpdated = true; |
| } else { |
| maxNumberOfRepayments = loanProduct.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) > 0) { |
| if (minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) > 0) { |
| 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) > 0) { |
| baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments) |
| .notLessThanMin(minNumberOfRepayments); |
| } |
| } |
| } |
| |
| private void validateNominalInterestRatePerPeriodMinMaxConstraint(final JsonElement element, final LoanProduct loanProduct, |
| final DataValidatorBuilder baseDataValidator) { |
| |
| if ((this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element) |
| && this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true) |
| || loanProduct.isLinkedToFloatingInterestRate()) { |
| return; |
| } |
| boolean iRPUpdated = false; |
| boolean minIRPUpdated = false; |
| boolean maxIRPUpdated = false; |
| final String interestRatePerPeriodParameterName = "interestRatePerPeriod"; |
| BigDecimal interestRatePerPeriod = null; |
| |
| if (this.fromApiJsonHelper.parameterExists(interestRatePerPeriodParameterName, element)) { |
| interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(interestRatePerPeriodParameterName, element); |
| iRPUpdated = true; |
| } else { |
| interestRatePerPeriod = loanProduct.getNominalInterestRatePerPeriod(); |
| } |
| |
| final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod"; |
| BigDecimal minInterestRatePerPeriod = null; |
| if (this.fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) { |
| minInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName, |
| element); |
| minIRPUpdated = true; |
| } else { |
| minInterestRatePerPeriod = loanProduct.getMinNominalInterestRatePerPeriod(); |
| } |
| |
| final String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod"; |
| BigDecimal maxInterestRatePerPeriod = null; |
| if (this.fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) { |
| maxInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName, |
| element); |
| maxIRPUpdated = true; |
| } else { |
| maxInterestRatePerPeriod = loanProduct.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); |
| } |
| |
| private boolean isAccrualBasedAccounting(final Integer accountingRuleType) { |
| return isUpfrontAccrualAccounting(accountingRuleType) || isPeriodicAccounting(accountingRuleType); |
| } |
| |
| private boolean isUpfrontAccrualAccounting(final Integer accountingRuleType) { |
| return AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(accountingRuleType); |
| } |
| |
| private boolean isPeriodicAccounting(final Integer accountingRuleType) { |
| return AccountingRuleType.ACCRUAL_PERIODIC.getValue().equals(accountingRuleType); |
| } |
| |
| private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { |
| if (!dataValidationErrors.isEmpty()) { |
| throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", |
| dataValidationErrors); |
| } |
| } |
| |
| private void validateBorrowerCycleVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator) { |
| validateBorrowerCyclePrincipalVariations(element, baseDataValidator); |
| validateBorrowerCycleRepaymentVariations(element, baseDataValidator); |
| validateBorrowerCycleInterestVariations(element, baseDataValidator); |
| } |
| |
| private void validateBorrowerCyclePrincipalVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator) { |
| |
| validateBorrowerCycleVariations(element, baseDataValidator, |
| LoanProductConstants.PRINCIPAL_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.PRINCIPAL_PER_CYCLE_PARAMETER_NAME, LoanProductConstants.MIN_PRINCIPAL_PER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.MIN_PRINCIPAL_PER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.PRINCIPAL_VALUE_USAGE_CONDITION_PARAM_NAME, LoanProductConstants.PRINCIPAL_CYCLE_NUMBERS_PARAM_NAME); |
| } |
| |
| private void validateBorrowerCycleRepaymentVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator) { |
| validateBorrowerCycleVariations(element, baseDataValidator, |
| LoanProductConstants.NUMBER_OF_REPAYMENT_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.NUMBER_OF_REPAYMENTS_PER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.MIN_NUMBER_OF_REPAYMENTS_PER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.MAX_NUMBER_OF_REPAYMENTS_PER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.REPAYMENT_VALUE_USAGE_CONDITION_PARAM_NAME, LoanProductConstants.REPAYMENT_CYCLE_NUMBER_PARAM_NAME); |
| } |
| |
| private void validateBorrowerCycleInterestVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator) { |
| validateBorrowerCycleVariations(element, baseDataValidator, |
| LoanProductConstants.INTEREST_RATE_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.INTEREST_RATE_PER_PERIOD_PER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.MIN_INTEREST_RATE_PER_PERIOD_PER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.MAX_INTEREST_RATE_PER_PERIOD_PER_CYCLE_PARAMETER_NAME, |
| LoanProductConstants.INTEREST_RATE_VALUE_USAGE_CONDITION_PARAM_NAME, |
| LoanProductConstants.INTEREST_RATE_VALUE_USAGE_CONDITION_PARAM_NAME); |
| } |
| |
| private void validateBorrowerCycleVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator, |
| final String variationParameterName, final String defaultParameterName, final String minParameterName, |
| final String maxParameterName, final String valueUsageConditionParamName, final String cycleNumbersParamName) { |
| final JsonObject topLevelJsonElement = element.getAsJsonObject(); |
| final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); |
| Integer lastCycleNumber = 0; |
| LoanProductValueConditionType lastConditionType = LoanProductValueConditionType.EQUAL; |
| if (this.fromApiJsonHelper.parameterExists(variationParameterName, element)) { |
| final JsonArray variationArray = this.fromApiJsonHelper.extractJsonArrayNamed(variationParameterName, element); |
| if (variationArray != null && variationArray.size() > 0) { |
| int i = 0; |
| do { |
| final JsonObject jsonObject = variationArray.get(i).getAsJsonObject(); |
| |
| BigDecimal defaultValue = this.fromApiJsonHelper |
| .extractBigDecimalNamed(LoanProductConstants.DEFAULT_VALUE_PARAMETER_NAME, jsonObject, locale); |
| BigDecimal minValue = this.fromApiJsonHelper.extractBigDecimalNamed(LoanProductConstants.MIN_VALUE_PARAMETER_NAME, |
| jsonObject, locale); |
| BigDecimal maxValue = this.fromApiJsonHelper.extractBigDecimalNamed(LoanProductConstants.MAX_VALUE_PARAMETER_NAME, |
| jsonObject, locale); |
| Integer cycleNumber = this.fromApiJsonHelper.extractIntegerNamed(LoanProductConstants.BORROWER_CYCLE_NUMBER_PARAM_NAME, |
| jsonObject, locale); |
| Integer valueUsageCondition = this.fromApiJsonHelper |
| .extractIntegerNamed(LoanProductConstants.VALUE_CONDITION_TYPE_PARAM_NAME, jsonObject, locale); |
| |
| baseDataValidator.reset().parameter(defaultParameterName).value(defaultValue).notBlank(); |
| if (minValue != null) { |
| baseDataValidator.reset().parameter(minParameterName).value(minValue).notGreaterThanMax(maxValue); |
| } |
| |
| if (maxValue != null) { |
| baseDataValidator.reset().parameter(maxParameterName).value(maxValue).notLessThanMin(minValue); |
| } |
| if ((minValue != null && minValue.compareTo(BigDecimal.ZERO) > 0) |
| && (maxValue != null && maxValue.compareTo(BigDecimal.ZERO) > 0)) { |
| baseDataValidator.reset().parameter(defaultParameterName).value(defaultValue).inMinAndMaxAmountRange(minValue, |
| maxValue); |
| } else { |
| if (minValue != null && minValue.compareTo(BigDecimal.ZERO) > 0) { |
| baseDataValidator.reset().parameter(defaultParameterName).value(defaultValue).notLessThanMin(minValue); |
| } else if (maxValue != null && maxValue.compareTo(BigDecimal.ZERO) > 0) { |
| baseDataValidator.reset().parameter(defaultParameterName).value(defaultValue).notGreaterThanMax(maxValue); |
| } |
| } |
| |
| LoanProductValueConditionType conditionType = LoanProductValueConditionType.INVALID; |
| if (valueUsageCondition != null) { |
| conditionType = LoanProductValueConditionType.fromInt(valueUsageCondition); |
| } |
| baseDataValidator.reset().parameter(valueUsageConditionParamName).value(valueUsageCondition).notNull().inMinMaxRange( |
| LoanProductValueConditionType.EQUAL.getValue(), LoanProductValueConditionType.GREATERTHAN.getValue()); |
| if (lastConditionType.equals(LoanProductValueConditionType.EQUAL) |
| && conditionType.equals(LoanProductValueConditionType.GREATERTHAN)) { |
| if (lastCycleNumber == 0) { |
| baseDataValidator.reset().parameter(cycleNumbersParamName) |
| .failWithCode(LoanProductConstants.VALUE_CONDITION_START_WITH_ERROR); |
| lastCycleNumber = 1; |
| } |
| baseDataValidator.reset().parameter(cycleNumbersParamName).value(cycleNumber).notNull() |
| .integerSameAsNumber(lastCycleNumber); |
| } else if (lastConditionType.equals(LoanProductValueConditionType.EQUAL)) { |
| baseDataValidator.reset().parameter(cycleNumbersParamName).value(cycleNumber).notNull() |
| .integerSameAsNumber(lastCycleNumber + 1); |
| } else if (lastConditionType.equals(LoanProductValueConditionType.GREATERTHAN)) { |
| baseDataValidator.reset().parameter(cycleNumbersParamName).value(cycleNumber).notNull() |
| .integerGreaterThanNumber(lastCycleNumber); |
| } |
| if (conditionType != null) { |
| lastConditionType = conditionType; |
| } |
| if (cycleNumber != null) { |
| lastCycleNumber = cycleNumber; |
| } |
| i++; |
| } while (i < variationArray.size()); |
| if (!lastConditionType.equals(LoanProductValueConditionType.GREATERTHAN)) { |
| baseDataValidator.reset().parameter(cycleNumbersParamName) |
| .failWithCode(LoanProductConstants.VALUE_CONDITION_END_WITH_ERROR); |
| } |
| } |
| |
| } |
| |
| } |
| |
| private void validateGuaranteeParams(final JsonElement element, final DataValidatorBuilder baseDataValidator, |
| final LoanProduct loanProduct) { |
| BigDecimal mandatoryGuarantee = BigDecimal.ZERO; |
| BigDecimal minimumGuaranteeFromOwnFunds = BigDecimal.ZERO; |
| BigDecimal minimumGuaranteeFromGuarantor = BigDecimal.ZERO; |
| if (loanProduct != null) { |
| mandatoryGuarantee = loanProduct.getLoanProductGuaranteeDetails().getMandatoryGuarantee(); |
| minimumGuaranteeFromOwnFunds = loanProduct.getLoanProductGuaranteeDetails().getMinimumGuaranteeFromOwnFunds(); |
| minimumGuaranteeFromGuarantor = loanProduct.getLoanProductGuaranteeDetails().getMinimumGuaranteeFromGuarantor(); |
| } |
| |
| if (loanProduct == null || this.fromApiJsonHelper.parameterExists(LoanProductConstants.mandatoryGuaranteeParamName, element)) { |
| mandatoryGuarantee = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanProductConstants.mandatoryGuaranteeParamName, |
| element); |
| baseDataValidator.reset().parameter(LoanProductConstants.mandatoryGuaranteeParamName).value(mandatoryGuarantee).notNull(); |
| if (mandatoryGuarantee == null) { |
| mandatoryGuarantee = BigDecimal.ZERO; |
| } |
| } |
| |
| if (loanProduct == null |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumGuaranteeFromGuarantorParamName, element)) { |
| minimumGuaranteeFromGuarantor = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(LoanProductConstants.minimumGuaranteeFromGuarantorParamName, element); |
| if (minimumGuaranteeFromGuarantor == null) { |
| minimumGuaranteeFromGuarantor = BigDecimal.ZERO; |
| } |
| } |
| |
| if (loanProduct == null |
| || this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, element)) { |
| minimumGuaranteeFromOwnFunds = this.fromApiJsonHelper |
| .extractBigDecimalWithLocaleNamed(LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, element); |
| if (minimumGuaranteeFromOwnFunds == null) { |
| minimumGuaranteeFromOwnFunds = BigDecimal.ZERO; |
| } |
| } |
| |
| if (mandatoryGuarantee.compareTo(minimumGuaranteeFromOwnFunds.add(minimumGuaranteeFromGuarantor)) < 0) { |
| baseDataValidator.parameter(LoanProductConstants.mandatoryGuaranteeParamName) |
| .failWithCode("must.be.greter.than.sum.of.min.funds"); |
| } |
| |
| } |
| |
| private void validatePartialPeriodSupport(final Integer interestCalculationPeriodType, final DataValidatorBuilder baseDataValidator, |
| final JsonElement element, final LoanProduct loanProduct) { |
| if (interestCalculationPeriodType != null) { |
| final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod |
| .fromInt(interestCalculationPeriodType); |
| boolean considerPartialPeriodUpdates = interestCalculationPeriodMethod.isDaily(); |
| |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, |
| element)) { |
| final Boolean considerPartialInterestEnabled = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, element); |
| baseDataValidator.reset().parameter(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME) |
| .value(considerPartialInterestEnabled).notNull().isOneOfTheseValues(true, false); |
| final boolean considerPartialPeriods = considerPartialInterestEnabled == null ? false : considerPartialInterestEnabled; |
| if (interestCalculationPeriodMethod.isDaily()) { |
| if (considerPartialPeriods) { |
| baseDataValidator.reset().parameter(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME) |
| .failWithCode("not.supported.for.daily.calcualtions"); |
| } |
| } else { |
| considerPartialPeriodUpdates = considerPartialPeriods; |
| } |
| } |
| |
| if (!considerPartialPeriodUpdates) { |
| Boolean isInterestRecalculationEnabled = null; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME, |
| element)) { |
| isInterestRecalculationEnabled = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME, element); |
| } else if (loanProduct != null) { |
| isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled(); |
| } |
| if (isInterestRecalculationEnabled != null && isInterestRecalculationEnabled) { |
| baseDataValidator.reset().parameter(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME) |
| .failWithCode("not.supported.for.selected.interest.calcualtion.type"); |
| } |
| |
| Boolean multiDisburseLoan = null; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME, element)) { |
| multiDisburseLoan = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME, |
| element); |
| } else if (loanProduct != null) { |
| isInterestRecalculationEnabled = loanProduct.isMultiDisburseLoan(); |
| } |
| if (multiDisburseLoan != null && multiDisburseLoan) { |
| baseDataValidator.reset().parameter(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME) |
| .failWithCode("not.supported.for.selected.interest.calcualtion.type"); |
| } |
| |
| Boolean variableInstallments = null; |
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.allowVariableInstallmentsParamName, element)) { |
| variableInstallments = this.fromApiJsonHelper |
| .extractBooleanNamed(LoanProductConstants.allowVariableInstallmentsParamName, element); |
| } else if (loanProduct != null) { |
| isInterestRecalculationEnabled = loanProduct.allowVariabeInstallments(); |
| } |
| if (variableInstallments != null && variableInstallments) { |
| baseDataValidator.reset().parameter(LoanProductConstants.allowVariableInstallmentsParamName) |
| .failWithCode("not.supported.for.selected.interest.calcualtion.type"); |
| } |
| |
| Boolean floatingInterestRates = null; |
| if (this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)) { |
| floatingInterestRates = this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element); |
| } else if (loanProduct != null) { |
| isInterestRecalculationEnabled = loanProduct.isLinkedToFloatingInterestRate(); |
| } |
| if (floatingInterestRates != null && floatingInterestRates) { |
| baseDataValidator.reset().parameter("isLinkedToFloatingInterestRates") |
| .failWithCode("not.supported.for.selected.interest.calcualtion.type"); |
| } |
| } |
| |
| } |
| } |
| } |