com.gst.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.gst.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper.java

Source

/**
 * 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 com.gst.portfolio.loanaccount.serialization;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import com.gst.infrastructure.core.data.ApiParameterError;
import com.gst.infrastructure.core.data.DataValidatorBuilder;
import com.gst.infrastructure.core.exception.InvalidJsonException;
import com.gst.infrastructure.core.exception.PlatformApiDataValidationException;
import com.gst.infrastructure.core.exception.UnsupportedParameterException;
import com.gst.infrastructure.core.serialization.FromJsonHelper;
import com.gst.portfolio.accountdetails.domain.AccountType;
import com.gst.portfolio.calendar.service.CalendarUtils;
import com.gst.portfolio.client.api.ClientApiConstants;
import com.gst.portfolio.loanaccount.api.LoanApiConstants;
import com.gst.portfolio.loanaccount.domain.Loan;
import com.gst.portfolio.loanaccount.domain.LoanCharge;
import com.gst.portfolio.loanproduct.LoanProductConstants;
import com.gst.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import com.gst.portfolio.loanproduct.domain.InterestMethod;
import com.gst.portfolio.loanproduct.domain.LoanProduct;
import com.gst.portfolio.savings.domain.SavingsAccount;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;

@Component
public final class LoanApplicationCommandFromApiJsonHelper {

    /**
     * The parameters supported for this command.
     */
    final Set<String> supportedParameters = new HashSet<>(Arrays.asList(LoanApiConstants.dateFormatParameterName,
            LoanApiConstants.localeParameterName, LoanApiConstants.idParameterName,
            LoanApiConstants.clientIdParameterName, LoanApiConstants.groupIdParameterName,
            LoanApiConstants.loanTypeParameterName, LoanApiConstants.productIdParameterName,
            LoanApiConstants.principalParamName, LoanApiConstants.loanTermFrequencyParameterName,
            LoanApiConstants.loanTermFrequencyTypeParameterName, LoanApiConstants.numberOfRepaymentsParameterName,
            LoanApiConstants.repaymentEveryParameterName, LoanApiConstants.repaymentFrequencyTypeParameterName,
            LoanApiConstants.repaymentFrequencyNthDayTypeParameterName,
            LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName,
            LoanApiConstants.interestRatePerPeriodParameterName, LoanApiConstants.amortizationTypeParameterName,
            LoanApiConstants.interestTypeParameterName, LoanApiConstants.isFloatingInterestRate,
            LoanApiConstants.interestRateDifferential, LoanApiConstants.interestCalculationPeriodTypeParameterName,
            LoanProductConstants.allowPartialPeriodInterestCalcualtionParamName,
            LoanApiConstants.interestRateFrequencyTypeParameterName, LoanApiConstants.disbursementDateParameterName,
            LoanApiConstants.repaymentsStartingFromDateParameterName,
            LoanApiConstants.graceOnPrincipalPaymentParameterName,
            LoanApiConstants.graceOnInterestPaymentParameterName,
            LoanApiConstants.graceOnInterestChargedParameterName,
            LoanApiConstants.interestChargedFromDateParameterName, LoanApiConstants.submittedOnDateParameterName,
            LoanApiConstants.submittedOnNoteParameterName, LoanApiConstants.accountNoParameterName,
            LoanApiConstants.externalIdParameterName, LoanApiConstants.fundIdParameterName,
            LoanApiConstants.loanOfficerIdParameterName, // optional
            LoanApiConstants.loanPurposeIdParameterName, LoanApiConstants.inArrearsToleranceParameterName,
            LoanApiConstants.chargesParameterName, LoanApiConstants.collateralParameterName, // optional
            LoanApiConstants.transactionProcessingStrategyIdParameterName, // settings
            LoanApiConstants.calendarIdParameterName, // optional
            LoanApiConstants.syncDisbursementWithMeetingParameterName, // optional
            LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName,
            LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
            LoanProductConstants.graceOnArrearsAgeingParameterName,
            LoanApiConstants.createStandingInstructionAtDisbursementParameterName, LoanApiConstants.isTopup,
            LoanApiConstants.loanIdToClose, LoanApiConstants.datatables));

    private final FromJsonHelper fromApiJsonHelper;
    private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;

    @Autowired
    public LoanApplicationCommandFromApiJsonHelper(final FromJsonHelper fromApiJsonHelper,
            final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper) {
        this.fromApiJsonHelper = fromApiJsonHelper;
        this.apiJsonHelper = apiJsonHelper;
    }

    public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans,
            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("loan");

        final JsonElement element = this.fromApiJsonHelper.parse(json);

        final String loanTypeParameterName = "loanType";
        final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
        baseDataValidator.reset().parameter(loanTypeParameterName).value(loanTypeStr).notNull();

        if (!StringUtils.isBlank(loanTypeStr)) {
            final AccountType loanType = AccountType.fromName(loanTypeStr);
            baseDataValidator.reset().parameter(loanTypeParameterName).value(loanType.getValue()).inMinMaxRange(1,
                    3);

            final Long clientId = this.fromApiJsonHelper.extractLongNamed("clientId", element);
            final Long groupId = this.fromApiJsonHelper.extractLongNamed("groupId", element);
            if (loanType.isIndividualAccount()) {
                baseDataValidator.reset().parameter("clientId").value(clientId).notNull().longGreaterThanZero();
                baseDataValidator.reset().parameter("groupId").value(groupId)
                        .mustBeBlankWhenParameterProvided("clientId", clientId);
            }

            if (loanType.isGroupAccount()) {
                baseDataValidator.reset().parameter("groupId").value(groupId).notNull().longGreaterThanZero();
                baseDataValidator.reset().parameter("clientId").value(clientId)
                        .mustBeBlankWhenParameterProvided("groupId", groupId);
            }

            if (loanType.isJLGAccount()) {
                baseDataValidator.reset().parameter("clientId").value(clientId).notNull().integerGreaterThanZero();
                baseDataValidator.reset().parameter("groupId").value(groupId).notNull().longGreaterThanZero();

                // if it is JLG loan that must have meeting details
                if (isMeetingMandatoryForJLGLoans) {
                    final String calendarIdParameterName = "calendarId";
                    final Long calendarId = this.fromApiJsonHelper.extractLongNamed(calendarIdParameterName,
                            element);
                    baseDataValidator.reset().parameter(calendarIdParameterName).value(calendarId).notNull()
                            .integerGreaterThanZero();

                    // if it is JLG loan then must have a value for
                    // syncDisbursement passed in
                    String syncDisbursementParameterName = "syncDisbursementWithMeeting";
                    final Boolean syncDisbursement = this.fromApiJsonHelper
                            .extractBooleanNamed(syncDisbursementParameterName, element);

                    if (syncDisbursement == null) {
                        baseDataValidator.reset().parameter(syncDisbursementParameterName).value(syncDisbursement)
                                .trueOrFalseRequired(false);
                    }
                }

            }

        }

        final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element);
        baseDataValidator.reset().parameter("productId").value(productId).notNull().integerGreaterThanZero();

        final String accountNoParameterName = "accountNo";
        if (this.fromApiJsonHelper.parameterExists(accountNoParameterName, element)) {
            final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParameterName, element);
            baseDataValidator.reset().parameter(accountNoParameterName).value(accountNo).ignoreIfNull()
                    .notExceedingLengthOf(20);
        }

        final String externalIdParameterName = "externalId";
        if (this.fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
            final String externalId = this.fromApiJsonHelper.extractStringNamed(externalIdParameterName, element);
            baseDataValidator.reset().parameter(externalIdParameterName).value(externalId).ignoreIfNull()
                    .notExceedingLengthOf(100);
        }

        final String fundIdParameterName = "fundId";
        if (this.fromApiJsonHelper.parameterExists(fundIdParameterName, element)) {
            final Long fundId = this.fromApiJsonHelper.extractLongNamed(fundIdParameterName, element);
            baseDataValidator.reset().parameter(fundIdParameterName).value(fundId).ignoreIfNull()
                    .integerGreaterThanZero();
        }

        final String loanOfficerIdParameterName = "loanOfficerId";
        if (this.fromApiJsonHelper.parameterExists(loanOfficerIdParameterName, element)) {
            final Long loanOfficerId = this.fromApiJsonHelper.extractLongNamed(loanOfficerIdParameterName, element);
            baseDataValidator.reset().parameter(loanOfficerIdParameterName).value(loanOfficerId).ignoreIfNull()
                    .integerGreaterThanZero();
        }

        final String loanPurposeIdParameterName = "loanPurposeId";
        if (this.fromApiJsonHelper.parameterExists(loanPurposeIdParameterName, element)) {
            final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed(loanPurposeIdParameterName, element);
            baseDataValidator.reset().parameter(loanPurposeIdParameterName).value(loanPurposeId).ignoreIfNull()
                    .integerGreaterThanZero();
        }

        final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
        baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();

        final String loanTermFrequencyParameterName = "loanTermFrequency";
        final Integer loanTermFrequency = this.fromApiJsonHelper
                .extractIntegerWithLocaleNamed(loanTermFrequencyParameterName, element);
        baseDataValidator.reset().parameter(loanTermFrequencyParameterName).value(loanTermFrequency).notNull()
                .integerGreaterThanZero();

        final String loanTermFrequencyTypeParameterName = "loanTermFrequencyType";
        final Integer loanTermFrequencyType = this.fromApiJsonHelper
                .extractIntegerSansLocaleNamed(loanTermFrequencyTypeParameterName, element);
        baseDataValidator.reset().parameter(loanTermFrequencyTypeParameterName).value(loanTermFrequencyType)
                .notNull().inMinMaxRange(0, 3);

        final String numberOfRepaymentsParameterName = "numberOfRepayments";
        final Integer numberOfRepayments = this.fromApiJsonHelper
                .extractIntegerWithLocaleNamed(numberOfRepaymentsParameterName, element);
        baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments).notNull()
                .integerGreaterThanZero();

        final String repaymentEveryParameterName = "repaymentEvery";
        final Integer repaymentEvery = this.fromApiJsonHelper
                .extractIntegerWithLocaleNamed(repaymentEveryParameterName, element);
        baseDataValidator.reset().parameter(repaymentEveryParameterName).value(repaymentEvery).notNull()
                .integerGreaterThanZero();

        final String repaymentEveryFrequencyTypeParameterName = "repaymentFrequencyType";
        final Integer repaymentEveryType = this.fromApiJsonHelper
                .extractIntegerSansLocaleNamed(repaymentEveryFrequencyTypeParameterName, element);
        baseDataValidator.reset().parameter(repaymentEveryFrequencyTypeParameterName).value(repaymentEveryType)
                .notNull().inMinMaxRange(0, 3);

        final String repaymentFrequencyNthDayTypeParameterName = "repaymentFrequencyNthDayType";
        final String repaymentFrequencyDayOfWeekTypeParameterName = "repaymentFrequencyDayOfWeekType";
        CalendarUtils.validateNthDayOfMonthFrequency(baseDataValidator, repaymentFrequencyNthDayTypeParameterName,
                repaymentFrequencyDayOfWeekTypeParameterName, element, this.fromApiJsonHelper);
        final String interestTypeParameterName = "interestType";
        final Integer interestType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestTypeParameterName,
                element);
        baseDataValidator.reset().parameter(interestTypeParameterName).value(interestType).notNull()
                .inMinMaxRange(0, 1);

        final String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType";
        final Integer interestCalculationPeriodType = this.fromApiJsonHelper
                .extractIntegerSansLocaleNamed(interestCalculationPeriodTypeParameterName, element);
        baseDataValidator.reset().parameter(interestCalculationPeriodTypeParameterName)
                .value(interestCalculationPeriodType).notNull().inMinMaxRange(0, 1);

        if (loanProduct.isLinkedToFloatingInterestRate()) {
            if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
                baseDataValidator.reset().parameter("interestRatePerPeriod").failWithCode(
                        "not.supported.loanproduct.linked.to.floating.rate",
                        "interestRatePerPeriod param is not supported, selected Loan Product is linked with floating interest rate.");
            }

            if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)) {
                final Boolean isFloatingInterestRate = this.fromApiJsonHelper
                        .extractBooleanNamed(LoanApiConstants.isFloatingInterestRate, element);
                if (isFloatingInterestRate != null && isFloatingInterestRate
                        && !loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed()) {
                    baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).failWithCode(
                            "true.not.supported.for.selected.loanproduct",
                            "isFloatingInterestRate value of true not supported for selected Loan Product.");
                }
            } else {
                baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate)
                        .trueOrFalseRequired(false);
            }

            if (interestType != null && interestType.equals(InterestMethod.FLAT.getValue())) {
                baseDataValidator.reset().parameter(interestTypeParameterName).failWithCode(
                        "should.be.0.for.selected.loan.product",
                        "interestType should be DECLINING_BALANCE for selected Loan Product as it is linked to floating rates.");
            }

            final String interestRateDifferentialParameterName = LoanApiConstants.interestRateDifferential;
            final BigDecimal interestRateDifferential = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(interestRateDifferentialParameterName, element);
            baseDataValidator.reset().parameter(interestRateDifferentialParameterName)
                    .value(interestRateDifferential).notNull().zeroOrPositiveAmount()
                    .inMinAndMaxAmountRange(loanProduct.getFloatingRates().getMinDifferentialLendingRate(),
                            loanProduct.getFloatingRates().getMaxDifferentialLendingRate());

        } else {

            if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)) {
                baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).failWithCode(
                        "not.supported.loanproduct.not.linked.to.floating.rate",
                        "isFloatingInterestRate param is not supported, selected Loan Product is not linked with floating interest rate.");
            }
            if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.interestRateDifferential, element)) {
                baseDataValidator.reset().parameter(LoanApiConstants.interestRateDifferential).failWithCode(
                        "not.supported.loanproduct.not.linked.to.floating.rate",
                        "interestRateDifferential param is not supported, selected Loan Product is not linked with floating interest rate.");
            }

            final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
            final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(interestRatePerPeriodParameterName, element);
            baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod)
                    .notNull().zeroOrPositiveAmount();

        }

        final String amortizationTypeParameterName = "amortizationType";
        final Integer amortizationType = this.fromApiJsonHelper
                .extractIntegerSansLocaleNamed(amortizationTypeParameterName, element);
        baseDataValidator.reset().parameter(amortizationTypeParameterName).value(amortizationType).notNull()
                .inMinMaxRange(0, 1);

        final String expectedDisbursementDateParameterName = "expectedDisbursementDate";
        final LocalDate expectedDisbursementDate = this.fromApiJsonHelper
                .extractLocalDateNamed(expectedDisbursementDateParameterName, element);
        baseDataValidator.reset().parameter(expectedDisbursementDateParameterName).value(expectedDisbursementDate)
                .notNull();

        // 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.graceOnArrearsAgeingParameterName, element);
        baseDataValidator.reset().parameter(LoanProductConstants.graceOnArrearsAgeingParameterName)
                .value(graceOnArrearsAgeing).zeroOrPositiveAmount();

        final String interestChargedFromDateParameterName = "interestChargedFromDate";
        if (this.fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
            final LocalDate interestChargedFromDate = this.fromApiJsonHelper
                    .extractLocalDateNamed(interestChargedFromDateParameterName, element);
            baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate)
                    .ignoreIfNull().notNull();
        }

        final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
        if (this.fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
            final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper
                    .extractLocalDateNamed(repaymentsStartingFromDateParameterName, element);
            baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName)
                    .value(repaymentsStartingFromDate).ignoreIfNull().notNull();
        }

        final String inArrearsToleranceParameterName = "inArrearsTolerance";
        if (this.fromApiJsonHelper.parameterExists(inArrearsToleranceParameterName, element)) {
            final BigDecimal inArrearsTolerance = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(inArrearsToleranceParameterName, element);
            baseDataValidator.reset().parameter(inArrearsToleranceParameterName).value(inArrearsTolerance)
                    .ignoreIfNull().zeroOrPositiveAmount();
        }

        final String submittedOnDateParameterName = "submittedOnDate";
        final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParameterName,
                element);
        if (submittedOnDate == null) {
            baseDataValidator.reset().parameter(submittedOnDateParameterName).value(submittedOnDate).notNull();
        }

        final String submittedOnNoteParameterName = "submittedOnNote";
        if (this.fromApiJsonHelper.parameterExists(submittedOnNoteParameterName, element)) {
            final String submittedOnNote = this.fromApiJsonHelper.extractStringNamed(submittedOnNoteParameterName,
                    element);
            baseDataValidator.reset().parameter(submittedOnNoteParameterName).value(submittedOnNote).ignoreIfNull()
                    .notExceedingLengthOf(500);
        }

        final String transactionProcessingStrategyIdParameterName = "transactionProcessingStrategyId";
        final Long transactionProcessingStrategyId = this.fromApiJsonHelper
                .extractLongNamed(transactionProcessingStrategyIdParameterName, element);
        baseDataValidator.reset().parameter(transactionProcessingStrategyIdParameterName)
                .value(transactionProcessingStrategyId).notNull().integerGreaterThanZero();

        final String linkAccountIdParameterName = "linkAccountId";
        if (this.fromApiJsonHelper.parameterExists(linkAccountIdParameterName, element)) {
            final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element);
            baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).ignoreIfNull()
                    .longGreaterThanZero();
        }

        final String createSiAtDisbursementParameterName = "createStandingInstructionAtDisbursement";
        if (this.fromApiJsonHelper.parameterExists(createSiAtDisbursementParameterName, element)) {
            final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper
                    .extractBooleanNamed(createSiAtDisbursementParameterName, element);
            final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element);

            if (createStandingInstructionAtDisbursement) {
                baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).notNull()
                        .longGreaterThanZero();
            }
        }

        // charges
        final String chargesParameterName = "charges";
        if (element.isJsonObject() && this.fromApiJsonHelper.parameterExists(chargesParameterName, element)) {
            final JsonObject topLevelJsonElement = element.getAsJsonObject();
            final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
            final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);

            if (topLevelJsonElement.get(chargesParameterName).isJsonArray()) {
                final Type arrayObjectParameterTypeOfMap = new TypeToken<Map<String, Object>>() {
                }.getType();
                final Set<String> supportedParameters = new HashSet<>(Arrays.asList("id", "chargeId", "amount",
                        "chargeTimeType", "chargeCalculationType", "dueDate"));

                final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
                for (int i = 1; i <= array.size(); i++) {

                    final JsonObject loanChargeElement = array.get(i - 1).getAsJsonObject();
                    final String arrayObjectJson = this.fromApiJsonHelper.toJson(loanChargeElement);
                    this.fromApiJsonHelper.checkForUnsupportedParameters(arrayObjectParameterTypeOfMap,
                            arrayObjectJson, supportedParameters);

                    final Long chargeId = this.fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
                    baseDataValidator.reset().parameter("charges").parameterAtIndexArray("chargeId", i)
                            .value(chargeId).notNull().integerGreaterThanZero();

                    final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed("amount",
                            loanChargeElement, locale);
                    baseDataValidator.reset().parameter("charges").parameterAtIndexArray("amount", i).value(amount)
                            .notNull().positiveAmount();

                    this.fromApiJsonHelper.extractLocalDateNamed("dueDate", loanChargeElement, dateFormat, locale);
                }
            }
        }

        // collateral
        final String collateralParameterName = "collateral";
        if (element.isJsonObject() && this.fromApiJsonHelper.parameterExists(collateralParameterName, element)) {
            final JsonObject topLevelJsonElement = element.getAsJsonObject();
            final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
            if (topLevelJsonElement.get("collateral").isJsonArray()) {

                final Type collateralParameterTypeOfMap = new TypeToken<Map<String, Object>>() {
                }.getType();
                final Set<String> supportedParameters = new HashSet<>(
                        Arrays.asList("id", "type", "value", "description"));
                final JsonArray array = topLevelJsonElement.get("collateral").getAsJsonArray();
                for (int i = 1; i <= array.size(); i++) {
                    final JsonObject collateralItemElement = array.get(i - 1).getAsJsonObject();

                    final String collateralJson = this.fromApiJsonHelper.toJson(collateralItemElement);
                    this.fromApiJsonHelper.checkForUnsupportedParameters(collateralParameterTypeOfMap,
                            collateralJson, supportedParameters);

                    final Long collateralTypeId = this.fromApiJsonHelper.extractLongNamed("type",
                            collateralItemElement);
                    baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("type", i)
                            .value(collateralTypeId).notNull().integerGreaterThanZero();

                    final BigDecimal collateralValue = this.fromApiJsonHelper.extractBigDecimalNamed("value",
                            collateralItemElement, locale);
                    baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("value", i)
                            .value(collateralValue).ignoreIfNull().positiveAmount();

                    final String description = this.fromApiJsonHelper.extractStringNamed("description",
                            collateralItemElement);
                    baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("description", i)
                            .value(description).notBlank().notExceedingLengthOf(500);

                }
            } else {
                baseDataValidator.reset().parameter(collateralParameterName).expectedArrayButIsNot();
            }
        }

        if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.emiAmountParameterName, element)) {
            if (!(loanProduct.canDefineInstallmentAmount() || loanProduct.isMultiDisburseLoan())) {
                List<String> unsupportedParameterList = new ArrayList<>();
                unsupportedParameterList.add(LoanApiConstants.emiAmountParameterName);
                throw new UnsupportedParameterException(unsupportedParameterList);
            }
            final BigDecimal emiAnount = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName, element);
            baseDataValidator.reset().parameter(LoanApiConstants.emiAmountParameterName).value(emiAnount)
                    .ignoreIfNull().positiveAmount();
        }
        if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.maxOutstandingBalanceParameterName, element)) {
            final BigDecimal maxOutstandingBalance = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(LoanApiConstants.maxOutstandingBalanceParameterName, element);
            baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName)
                    .value(maxOutstandingBalance).ignoreIfNull().positiveAmount();
        }

        if (loanProduct.canUseForTopup()) {
            if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isTopup, element)) {
                final Boolean isTopup = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isTopup,
                        element);
                baseDataValidator.reset().parameter(LoanApiConstants.isTopup).value(isTopup)
                        .validateForBooleanValue();

                if (isTopup != null && isTopup) {
                    final Long loanId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.loanIdToClose,
                            element);
                    baseDataValidator.reset().parameter(LoanApiConstants.loanIdToClose).value(loanId).notNull()
                            .longGreaterThanZero();
                }
            }
        }
        if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.datatables, element)) {
            final JsonArray datatables = this.fromApiJsonHelper.extractJsonArrayNamed(LoanApiConstants.datatables,
                    element);
            baseDataValidator.reset().parameter(LoanApiConstants.datatables).value(datatables).notNull()
                    .jsonArrayNotEmpty();
        }

        validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
        validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct);
        if (!dataValidationErrors.isEmpty()) {
            throw new PlatformApiDataValidationException(dataValidationErrors);
        }
    }

    public void validateForModify(final String json, final LoanProduct loanProduct,
            final Loan existingLoanApplication) {
        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("loan");
        final JsonElement element = this.fromApiJsonHelper.parse(json);
        boolean atLeastOneParameterPassedForUpdate = false;

        final String clientIdParameterName = "clientId";
        if (this.fromApiJsonHelper.parameterExists(clientIdParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Long clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParameterName, element);
            baseDataValidator.reset().parameter(clientIdParameterName).value(clientId).notNull()
                    .integerGreaterThanZero();
        }

        final String groupIdParameterName = "groupId";
        if (this.fromApiJsonHelper.parameterExists(groupIdParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Long groupId = this.fromApiJsonHelper.extractLongNamed(groupIdParameterName, element);
            baseDataValidator.reset().parameter(groupIdParameterName).value(groupId).notNull()
                    .integerGreaterThanZero();
        }

        final String productIdParameterName = "productId";
        if (this.fromApiJsonHelper.parameterExists(productIdParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Long productId = this.fromApiJsonHelper.extractLongNamed(productIdParameterName, element);
            baseDataValidator.reset().parameter(productIdParameterName).value(productId).notNull()
                    .integerGreaterThanZero();
        }

        final String accountNoParameterName = "accountNo";
        if (this.fromApiJsonHelper.parameterExists(accountNoParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParameterName, element);
            baseDataValidator.reset().parameter(accountNoParameterName).value(accountNo).notBlank()
                    .notExceedingLengthOf(20);
        }

        final String externalIdParameterName = "externalId";
        if (this.fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final String externalId = this.fromApiJsonHelper.extractStringNamed(externalIdParameterName, element);
            baseDataValidator.reset().parameter(externalIdParameterName).value(externalId).ignoreIfNull()
                    .notExceedingLengthOf(100);
        }

        final String fundIdParameterName = "fundId";
        if (this.fromApiJsonHelper.parameterExists(fundIdParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Long fundId = this.fromApiJsonHelper.extractLongNamed(fundIdParameterName, element);
            baseDataValidator.reset().parameter(fundIdParameterName).value(fundId).ignoreIfNull()
                    .integerGreaterThanZero();
        }

        final String loanOfficerIdParameterName = "loanOfficerId";
        if (this.fromApiJsonHelper.parameterExists(loanOfficerIdParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Long loanOfficerId = this.fromApiJsonHelper.extractLongNamed(loanOfficerIdParameterName, element);
            baseDataValidator.reset().parameter(loanOfficerIdParameterName).value(loanOfficerId).ignoreIfNull()
                    .integerGreaterThanZero();
        }

        final String transactionProcessingStrategyIdParameterName = "transactionProcessingStrategyId";
        if (this.fromApiJsonHelper.parameterExists(transactionProcessingStrategyIdParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Long transactionProcessingStrategyId = this.fromApiJsonHelper
                    .extractLongNamed(transactionProcessingStrategyIdParameterName, element);
            baseDataValidator.reset().parameter(transactionProcessingStrategyIdParameterName)
                    .value(transactionProcessingStrategyId).notNull().integerGreaterThanZero();
        }

        final String principalParameterName = "principal";
        BigDecimal principal = null;
        if (this.fromApiJsonHelper.parameterExists(principalParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(principalParameterName, element);
            baseDataValidator.reset().parameter(principalParameterName).value(principal).notNull().positiveAmount();
        }

        final String inArrearsToleranceParameterName = "inArrearsTolerance";
        if (this.fromApiJsonHelper.parameterExists(inArrearsToleranceParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final BigDecimal inArrearsTolerance = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(inArrearsToleranceParameterName, element);
            baseDataValidator.reset().parameter(inArrearsToleranceParameterName).value(inArrearsTolerance)
                    .ignoreIfNull().zeroOrPositiveAmount();
        }

        final String loanTermFrequencyParameterName = "loanTermFrequency";
        if (this.fromApiJsonHelper.parameterExists(loanTermFrequencyParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Integer loanTermFrequency = this.fromApiJsonHelper
                    .extractIntegerWithLocaleNamed(loanTermFrequencyParameterName, element);
            baseDataValidator.reset().parameter(loanTermFrequencyParameterName).value(loanTermFrequency).notNull()
                    .integerGreaterThanZero();
        }

        final String loanTermFrequencyTypeParameterName = "loanTermFrequencyType";
        if (this.fromApiJsonHelper.parameterExists(loanTermFrequencyTypeParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Integer loanTermFrequencyType = this.fromApiJsonHelper
                    .extractIntegerWithLocaleNamed(loanTermFrequencyTypeParameterName, element);
            baseDataValidator.reset().parameter(loanTermFrequencyTypeParameterName).value(loanTermFrequencyType)
                    .notNull().inMinMaxRange(0, 3);
        }

        final String numberOfRepaymentsParameterName = "numberOfRepayments";
        if (this.fromApiJsonHelper.parameterExists(numberOfRepaymentsParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Integer numberOfRepayments = this.fromApiJsonHelper
                    .extractIntegerWithLocaleNamed(numberOfRepaymentsParameterName, element);
            baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments).notNull()
                    .integerGreaterThanZero();
        }

        final String repaymentEveryParameterName = "repaymentEvery";
        if (this.fromApiJsonHelper.parameterExists(repaymentEveryParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Integer repaymentEvery = this.fromApiJsonHelper
                    .extractIntegerWithLocaleNamed(repaymentEveryParameterName, element);
            baseDataValidator.reset().parameter(repaymentEveryParameterName).value(repaymentEvery).notNull()
                    .integerGreaterThanZero();
        }

        final String repaymentEveryTypeParameterName = "repaymentFrequencyType";
        if (this.fromApiJsonHelper.parameterExists(repaymentEveryTypeParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Integer repaymentEveryType = this.fromApiJsonHelper
                    .extractIntegerWithLocaleNamed(repaymentEveryTypeParameterName, element);
            baseDataValidator.reset().parameter(repaymentEveryTypeParameterName).value(repaymentEveryType).notNull()
                    .inMinMaxRange(0, 3);
        }
        final String repaymentFrequencyNthDayTypeParameterName = "repaymentFrequencyNthDayType";
        final String repaymentFrequencyDayOfWeekTypeParameterName = "repaymentFrequencyDayOfWeekType";
        CalendarUtils.validateNthDayOfMonthFrequency(baseDataValidator, repaymentFrequencyNthDayTypeParameterName,
                repaymentFrequencyDayOfWeekTypeParameterName, element, this.fromApiJsonHelper);

        final String interestTypeParameterName = "interestType";
        Integer interestType = null;
        if (this.fromApiJsonHelper.parameterExists(interestTypeParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            interestType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(interestTypeParameterName, element);
            baseDataValidator.reset().parameter(interestTypeParameterName).value(interestType).notNull()
                    .inMinMaxRange(0, 1);
        }

        if (loanProduct.isLinkedToFloatingInterestRate()) {
            if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
                baseDataValidator.reset().parameter("interestRatePerPeriod").failWithCode(
                        "not.supported.loanproduct.linked.to.floating.rate",
                        "interestRatePerPeriod param is not supported, selected Loan Product is linked with floating interest rate.");
            }

            Boolean isFloatingInterestRate = existingLoanApplication.getIsFloatingInterestRate();
            if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)) {
                isFloatingInterestRate = this.fromApiJsonHelper
                        .extractBooleanNamed(LoanApiConstants.isFloatingInterestRate, element);
                atLeastOneParameterPassedForUpdate = true;
            }
            if (isFloatingInterestRate != null) {
                if (isFloatingInterestRate
                        && !loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed()) {
                    baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).failWithCode(
                            "true.not.supported.for.selected.loanproduct",
                            "isFloatingInterestRate value of true not supported for selected Loan Product.");
                }
            } else {
                baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate)
                        .trueOrFalseRequired(false);
            }

            if (interestType == null) {
                interestType = existingLoanApplication.getLoanProductRelatedDetail().getInterestMethod().getValue();
            }
            if (interestType != null && interestType.equals(InterestMethod.FLAT.getValue())) {
                baseDataValidator.reset().parameter(interestTypeParameterName).failWithCode(
                        "should.be.0.for.selected.loan.product",
                        "interestType should be DECLINING_BALANCE for selected Loan Product as it is linked to floating rates.");
            }

            final String interestRateDifferentialParameterName = LoanApiConstants.interestRateDifferential;
            BigDecimal interestRateDifferential = existingLoanApplication.getInterestRateDifferential();
            if (this.fromApiJsonHelper.parameterExists(interestRateDifferentialParameterName, element)) {
                interestRateDifferential = this.fromApiJsonHelper
                        .extractBigDecimalWithLocaleNamed(interestRateDifferentialParameterName, element);
                atLeastOneParameterPassedForUpdate = true;
            }
            baseDataValidator.reset().parameter(interestRateDifferentialParameterName)
                    .value(interestRateDifferential).notNull().zeroOrPositiveAmount()
                    .inMinAndMaxAmountRange(loanProduct.getFloatingRates().getMinDifferentialLendingRate(),
                            loanProduct.getFloatingRates().getMaxDifferentialLendingRate());

        } else {

            if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)) {
                baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).failWithCode(
                        "not.supported.loanproduct.not.linked.to.floating.rate",
                        "isFloatingInterestRate param is not supported, selected Loan Product is not linked with floating interest rate.");
            }
            if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.interestRateDifferential, element)) {
                baseDataValidator.reset().parameter(LoanApiConstants.interestRateDifferential).failWithCode(
                        "not.supported.loanproduct.not.linked.to.floating.rate",
                        "interestRateDifferential param is not supported, selected Loan Product is not linked with floating interest rate.");
            }

            final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
            BigDecimal interestRatePerPeriod = existingLoanApplication.getLoanProductRelatedDetail()
                    .getNominalInterestRatePerPeriod();
            if (this.fromApiJsonHelper.parameterExists(interestRatePerPeriodParameterName, element)) {
                this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(interestRatePerPeriodParameterName,
                        element);
                atLeastOneParameterPassedForUpdate = true;
            }
            baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod)
                    .notNull().zeroOrPositiveAmount();

        }

        Integer interestCalculationPeriodType = loanProduct.getLoanProductRelatedDetail()
                .getInterestCalculationPeriodMethod().getValue();
        final String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType";
        if (this.fromApiJsonHelper.parameterExists(interestCalculationPeriodTypeParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            interestCalculationPeriodType = this.fromApiJsonHelper
                    .extractIntegerWithLocaleNamed(interestCalculationPeriodTypeParameterName, element);
            baseDataValidator.reset().parameter(interestCalculationPeriodTypeParameterName)
                    .value(interestCalculationPeriodType).notNull().inMinMaxRange(0, 1);
        }

        final String amortizationTypeParameterName = "amortizationType";
        if (this.fromApiJsonHelper.parameterExists(amortizationTypeParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Integer amortizationType = this.fromApiJsonHelper
                    .extractIntegerWithLocaleNamed(amortizationTypeParameterName, element);
            baseDataValidator.reset().parameter(amortizationTypeParameterName).value(amortizationType).notNull()
                    .inMinMaxRange(0, 1);
        }

        final String expectedDisbursementDateParameterName = "expectedDisbursementDate";
        LocalDate expectedDisbursementDate = null;
        if (this.fromApiJsonHelper.parameterExists(expectedDisbursementDateParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;

            final String expectedDisbursementDateStr = this.fromApiJsonHelper
                    .extractStringNamed(expectedDisbursementDateParameterName, element);
            baseDataValidator.reset().parameter(expectedDisbursementDateParameterName)
                    .value(expectedDisbursementDateStr).notBlank();

            expectedDisbursementDate = this.fromApiJsonHelper
                    .extractLocalDateNamed(expectedDisbursementDateParameterName, element);
            baseDataValidator.reset().parameter(expectedDisbursementDateParameterName)
                    .value(expectedDisbursementDate).notNull();
        }

        // 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.graceOnArrearsAgeingParameterName,
                element)) {
            final Integer graceOnArrearsAgeing = this.fromApiJsonHelper
                    .extractIntegerWithLocaleNamed(LoanProductConstants.graceOnArrearsAgeingParameterName, element);
            baseDataValidator.reset().parameter(LoanProductConstants.graceOnArrearsAgeingParameterName)
                    .value(graceOnArrearsAgeing).zeroOrPositiveAmount();
        }

        final String interestChargedFromDateParameterName = "interestChargedFromDate";
        if (this.fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final LocalDate interestChargedFromDate = this.fromApiJsonHelper
                    .extractLocalDateNamed(interestChargedFromDateParameterName, element);
            baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate)
                    .ignoreIfNull();
        }

        final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
        if (this.fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper
                    .extractLocalDateNamed(repaymentsStartingFromDateParameterName, element);
            baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName)
                    .value(repaymentsStartingFromDate).ignoreIfNull();
            if (!existingLoanApplication.getLoanTermVariations().isEmpty()) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "cannot.modify.application.due.to.variable.installments");
            }
        }

        final String submittedOnDateParameterName = "submittedOnDate";
        if (this.fromApiJsonHelper.parameterExists(submittedOnDateParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final LocalDate submittedOnDate = this.fromApiJsonHelper
                    .extractLocalDateNamed(submittedOnDateParameterName, element);
            baseDataValidator.reset().parameter(submittedOnDateParameterName).value(submittedOnDate).notNull();
        }

        final String submittedOnNoteParameterName = "submittedOnNote";
        if (this.fromApiJsonHelper.parameterExists(submittedOnNoteParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final String submittedOnNote = this.fromApiJsonHelper.extractStringNamed(submittedOnNoteParameterName,
                    element);
            baseDataValidator.reset().parameter(submittedOnNoteParameterName).value(submittedOnNote).ignoreIfNull()
                    .notExceedingLengthOf(500);
        }

        final String linkAccountIdParameterName = "linkAccountId";
        if (this.fromApiJsonHelper.parameterExists(submittedOnNoteParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;
            final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element);
            baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).ignoreIfNull()
                    .longGreaterThanZero();
        }

        // charges
        final String chargesParameterName = "charges";
        if (element.isJsonObject() && this.fromApiJsonHelper.parameterExists(chargesParameterName, element)) {
            atLeastOneParameterPassedForUpdate = true;

            final JsonObject topLevelJsonElement = element.getAsJsonObject();
            final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
            final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);

            if (topLevelJsonElement.get(chargesParameterName).isJsonArray()) {
                final Type arrayObjectParameterTypeOfMap = new TypeToken<Map<String, Object>>() {
                }.getType();
                final Set<String> supportedParameters = new HashSet<>(Arrays.asList("id", "chargeId", "amount",
                        "chargeTimeType", "chargeCalculationType", "dueDate"));

                final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
                for (int i = 1; i <= array.size(); i++) {

                    final JsonObject loanChargeElement = array.get(i - 1).getAsJsonObject();
                    final String arrayObjectJson = this.fromApiJsonHelper.toJson(loanChargeElement);
                    this.fromApiJsonHelper.checkForUnsupportedParameters(arrayObjectParameterTypeOfMap,
                            arrayObjectJson, supportedParameters);

                    final Long chargeId = this.fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
                    baseDataValidator.reset().parameter("charges").parameterAtIndexArray("chargeId", i)
                            .value(chargeId).notNull().integerGreaterThanZero();

                    final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed("amount",
                            loanChargeElement, locale);
                    baseDataValidator.reset().parameter("charges").parameterAtIndexArray("amount", i).value(amount)
                            .notNull().positiveAmount();

                    this.fromApiJsonHelper.extractLocalDateNamed("dueDate", loanChargeElement, dateFormat, locale);
                }
            }
        }

        // collateral
        final String collateralParameterName = "collateral";
        if (element.isJsonObject() && this.fromApiJsonHelper.parameterExists(collateralParameterName, element)) {
            final JsonObject topLevelJsonElement = element.getAsJsonObject();
            final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
            if (topLevelJsonElement.get("collateral").isJsonArray()) {

                final Type collateralParameterTypeOfMap = new TypeToken<Map<String, Object>>() {
                }.getType();
                final Set<String> supportedParameters = new HashSet<>(
                        Arrays.asList("id", "type", "value", "description"));
                final JsonArray array = topLevelJsonElement.get("collateral").getAsJsonArray();
                for (int i = 1; i <= array.size(); i++) {
                    final JsonObject collateralItemElement = array.get(i - 1).getAsJsonObject();

                    final String collateralJson = this.fromApiJsonHelper.toJson(collateralItemElement);
                    this.fromApiJsonHelper.checkForUnsupportedParameters(collateralParameterTypeOfMap,
                            collateralJson, supportedParameters);

                    final Long collateralTypeId = this.fromApiJsonHelper.extractLongNamed("type",
                            collateralItemElement);
                    baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("type", i)
                            .value(collateralTypeId).notNull().integerGreaterThanZero();

                    final BigDecimal collateralValue = this.fromApiJsonHelper.extractBigDecimalNamed("value",
                            collateralItemElement, locale);
                    baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("value", i)
                            .value(collateralValue).ignoreIfNull().positiveAmount();

                    final String description = this.fromApiJsonHelper.extractStringNamed("description",
                            collateralItemElement);
                    baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("description", i)
                            .value(description).notBlank().notExceedingLengthOf(500);

                }
            } else {
                baseDataValidator.reset().parameter(collateralParameterName).expectedArrayButIsNot();
            }
        }

        boolean meetingIdRequired = false;
        // validate syncDisbursement
        final String syncDisbursementParameterName = "syncDisbursementWithMeeting";
        if (this.fromApiJsonHelper.parameterExists(syncDisbursementParameterName, element)) {
            final Boolean syncDisbursement = this.fromApiJsonHelper
                    .extractBooleanNamed(syncDisbursementParameterName, element);
            if (syncDisbursement == null) {
                baseDataValidator.reset().parameter(syncDisbursementParameterName).value(syncDisbursement)
                        .trueOrFalseRequired(false);
            } else if (syncDisbursement.booleanValue()) {
                meetingIdRequired = true;
            }
        }

        final String calendarIdParameterName = "calendarId";
        // if disbursement is synced then must have a meeting (calendar)
        if (meetingIdRequired || this.fromApiJsonHelper.parameterExists(calendarIdParameterName, element)) {
            final Long calendarId = this.fromApiJsonHelper.extractLongNamed(calendarIdParameterName, element);
            baseDataValidator.reset().parameter(calendarIdParameterName).value(calendarId).notNull()
                    .integerGreaterThanZero();
        }

        if (!atLeastOneParameterPassedForUpdate) {
            final Object forceError = null;
            baseDataValidator.reset().anyOfNotNull(forceError);
        }

        if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.emiAmountParameterName, element)) {
            if (!(loanProduct.canDefineInstallmentAmount() || loanProduct.isMultiDisburseLoan())) {
                List<String> unsupportedParameterList = new ArrayList<>();
                unsupportedParameterList.add(LoanApiConstants.emiAmountParameterName);
                throw new UnsupportedParameterException(unsupportedParameterList);
            }
            final BigDecimal emiAnount = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName, element);
            baseDataValidator.reset().parameter(LoanApiConstants.emiAmountParameterName).value(emiAnount)
                    .ignoreIfNull().positiveAmount();
        }

        if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.maxOutstandingBalanceParameterName, element)) {
            final BigDecimal maxOutstandingBalance = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(LoanApiConstants.maxOutstandingBalanceParameterName, element);
            baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName)
                    .value(maxOutstandingBalance).ignoreIfNull().positiveAmount();
        }

        if (loanProduct.canUseForTopup()) {
            if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isTopup, element)) {
                final Boolean isTopup = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isTopup,
                        element);
                baseDataValidator.reset().parameter(LoanApiConstants.isTopup).value(isTopup).ignoreIfNull()
                        .validateForBooleanValue();

                if (isTopup != null && isTopup) {
                    final Long loanId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.loanIdToClose,
                            element);
                    baseDataValidator.reset().parameter(LoanApiConstants.loanIdToClose).value(loanId).notNull()
                            .longGreaterThanZero();
                }
            }
        }

        validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
        validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct);

        if (!dataValidationErrors.isEmpty()) {
            throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
                    "Validation errors exist.", dataValidationErrors);
        }
    }

    public void validateForUndo(final String json) {
        if (StringUtils.isBlank(json)) {
            throw new InvalidJsonException();
        }

        final Set<String> undoSupportedParameters = new HashSet<>(Arrays.asList("note"));
        final Type typeOfMap = new TypeToken<Map<String, Object>>() {
        }.getType();
        this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, undoSupportedParameters);

        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
        final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
                .resource("loanapplication.undo");
        final JsonElement element = this.fromApiJsonHelper.parse(json);

        final String note = "note";
        if (this.fromApiJsonHelper.parameterExists(note, element)) {
            final String noteText = this.fromApiJsonHelper.extractStringNamed(note, element);
            baseDataValidator.reset().parameter(note).value(noteText).notExceedingLengthOf(1000);
        }

        if (!dataValidationErrors.isEmpty()) {
            throw new PlatformApiDataValidationException(dataValidationErrors);
        }
    }

    public void validateMinMaxConstraintValues(final JsonElement element, final LoanProduct loanProduct) {

        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
        final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
                .resource("loan");

        final BigDecimal minPrincipal = loanProduct.getMinPrincipalAmount().getAmount();
        final BigDecimal maxPrincipal = loanProduct.getMaxPrincipalAmount().getAmount();
        final String principalParameterName = "principal";

        if (this.fromApiJsonHelper.parameterExists(principalParameterName, element)) {
            final BigDecimal principal = this.fromApiJsonHelper
                    .extractBigDecimalWithLocaleNamed(principalParameterName, element);
            baseDataValidator.reset().parameter(principalParameterName).value(principal).notNull().positiveAmount()
                    .inMinAndMaxAmountRange(minPrincipal, maxPrincipal);
        }

        if (!dataValidationErrors.isEmpty()) {
            throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
                    "Validation errors exist.", dataValidationErrors);
        }
    }

    public void validateLoanTermAndRepaidEveryValues(final Integer loanTermFrequency,
            final Integer loanTermFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
            final Integer repaymentEveryType, final Loan loan) {
        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
        this.apiJsonHelper.validateSelectedPeriodFrequencyTypeIsTheSame(dataValidationErrors, loanTermFrequency,
                loanTermFrequencyType, numberOfRepayments, repaymentEvery, repaymentEveryType);

        /**
         * For multi-disbursal loans where schedules are auto-generated based on
         * a fixed EMI, ensure the number of repayments is within the
         * permissible range defined by the loan product
         **/
        if (loan.getFixedEmiAmount() != null) {
            Integer minimumNoOfRepayments = loan.loanProduct().getMinNumberOfRepayments();
            Integer maximumNoOfRepayments = loan.loanProduct().getMaxNumberOfRepayments();
            Integer actualNumberOfRepayments = loan.getLoanRepaymentScheduleInstallmentsSize();
            // validate actual number of repayments is > minimum number of
            // repayments
            if (minimumNoOfRepayments != null && minimumNoOfRepayments != 0
                    && actualNumberOfRepayments < minimumNoOfRepayments) {
                final ApiParameterError error = ApiParameterError.generalError(
                        "validation.msg.loan.numberOfRepayments.lesser.than.minimumNumberOfRepayments",
                        "The total number of calculated repayments for this loan " + actualNumberOfRepayments
                                + " is lesser than the allowed minimum of " + minimumNoOfRepayments,
                        actualNumberOfRepayments, minimumNoOfRepayments);
                dataValidationErrors.add(error);
            }

            // validate actual number of repayments is < maximum number of
            // repayments
            if (maximumNoOfRepayments != null && maximumNoOfRepayments != 0
                    && actualNumberOfRepayments > maximumNoOfRepayments) {
                final ApiParameterError error = ApiParameterError.generalError(
                        "validation.msg.loan.numberOfRepayments.greater.than.maximumNumberOfRepayments",
                        "The total number of calculated repayments for this loan " + actualNumberOfRepayments
                                + " is greater than the allowed maximum of " + maximumNoOfRepayments,
                        actualNumberOfRepayments, maximumNoOfRepayments);
                dataValidationErrors.add(error);
            }

        }
        if (!dataValidationErrors.isEmpty()) {
            throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
                    "Validation errors exist.", dataValidationErrors);
        }
    }

    public void validatelinkedSavingsAccount(final SavingsAccount savingsAccount, final Loan loanApplication) {
        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
        if (savingsAccount.isNotActive()) {
            final ApiParameterError error = ApiParameterError.parameterError(
                    "validation.msg.loan.linked.savings.account.is.not.active",
                    "Linked Savings account with id:" + savingsAccount.getId() + " is not in active state",
                    "linkAccountId", savingsAccount.getId());
            dataValidationErrors.add(error);
        } else if (loanApplication.getClientId() != savingsAccount.clientId()) {
            final ApiParameterError error = ApiParameterError
                    .parameterError("validation.msg.loan.linked.savings.account.not.belongs.to.same.client",
                            "Linked Savings account with id:" + savingsAccount.getId()
                                    + " is not belongs to the same client",
                            "linkAccountId", savingsAccount.getId());
            dataValidationErrors.add(error);
        }
        if (!dataValidationErrors.isEmpty()) {
            throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
                    "Validation errors exist.", dataValidationErrors);
        }
    }

    private void validateDisbursementsAreDatewiseOrdered(JsonElement element,
            final DataValidatorBuilder baseDataValidator) {
        final JsonObject topLevelJsonElement = element.getAsJsonObject();
        final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
        final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
        final JsonArray variationArray = this.fromApiJsonHelper
                .extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName, element);
        if (variationArray != null) {
            for (int i = 0; i < variationArray.size(); i++) {
                final JsonObject jsonObject1 = variationArray.get(i).getAsJsonObject();
                if (jsonObject1.has(LoanApiConstants.disbursementDateParameterName)) {
                    LocalDate date1 = this.fromApiJsonHelper.extractLocalDateNamed(
                            LoanApiConstants.disbursementDateParameterName, jsonObject1, dateFormat, locale);

                    for (int j = i + 1; j < variationArray.size(); j++) {
                        final JsonObject jsonObject2 = variationArray.get(j).getAsJsonObject();
                        if (jsonObject2.has(LoanApiConstants.disbursementDateParameterName)) {
                            LocalDate date2 = this.fromApiJsonHelper.extractLocalDateNamed(
                                    LoanApiConstants.disbursementDateParameterName, jsonObject2, dateFormat,
                                    locale);
                            if (date1.isAfter(date2)) {
                                baseDataValidator.reset().parameter(LoanApiConstants.disbursementDataParameterName)
                                        .failWithCode(LoanApiConstants.DISBURSEMENT_DATES_NOT_IN_ORDER);
                            }
                        }
                    }
                }

            }
        }
    }

    public void validateLoanMultiDisbursementdate(final JsonElement element,
            final DataValidatorBuilder baseDataValidator, LocalDate expectedDisbursement,
            BigDecimal totalPrincipal) {

        this.validateDisbursementsAreDatewiseOrdered(element, baseDataValidator);

        final JsonObject topLevelJsonElement = element.getAsJsonObject();
        final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
        final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
        if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.disbursementDataParameterName, element)
                && expectedDisbursement != null && totalPrincipal != null) {
            BigDecimal tatalDisbursement = BigDecimal.ZERO;
            boolean isFirstinstallmentOnExpectedDisbursementDate = false;
            final JsonArray variationArray = this.fromApiJsonHelper
                    .extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName, element);
            List<LocalDate> expectedDisbursementDates = new ArrayList<>();
            if (variationArray != null && variationArray.size() > 0) {
                int i = 0;
                do {
                    final JsonObject jsonObject = variationArray.get(i).getAsJsonObject();
                    if (jsonObject.has(LoanApiConstants.disbursementDateParameterName)
                            && jsonObject.has(LoanApiConstants.disbursementPrincipalParameterName)) {
                        LocalDate expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed(
                                LoanApiConstants.disbursementDateParameterName, jsonObject, dateFormat, locale);
                        if (expectedDisbursementDates.contains(expectedDisbursementDate)) {
                            baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName)
                                    .failWithCode(LoanApiConstants.DISBURSEMENT_DATE_UNIQUE_ERROR);
                        }
                        if (expectedDisbursementDate.isBefore(expectedDisbursement)) {
                            baseDataValidator.reset().parameter(LoanApiConstants.disbursementDataParameterName)
                                    .failWithCode(LoanApiConstants.DISBURSEMENT_DATE_BEFORE_ERROR);
                        }
                        expectedDisbursementDates.add(expectedDisbursementDate);

                        BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalNamed(
                                LoanApiConstants.disbursementPrincipalParameterName, jsonObject, locale);
                        baseDataValidator.reset().parameter(LoanApiConstants.disbursementDataParameterName)
                                .parameterAtIndexArray(LoanApiConstants.disbursementPrincipalParameterName, i)
                                .value(principal).notBlank();
                        if (principal != null) {
                            tatalDisbursement = tatalDisbursement.add(principal);
                        }

                        baseDataValidator.reset().parameter(LoanApiConstants.disbursementDataParameterName)
                                .parameterAtIndexArray(LoanApiConstants.disbursementDateParameterName, i)
                                .value(expectedDisbursementDate).notNull();

                        if (expectedDisbursement.equals(expectedDisbursementDate)) {
                            isFirstinstallmentOnExpectedDisbursementDate = true;
                        }

                    }
                    i++;
                } while (i < variationArray.size());
                if (!isFirstinstallmentOnExpectedDisbursementDate) {
                    baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName)
                            .failWithCode(LoanApiConstants.DISBURSEMENT_DATE_START_WITH_ERROR);
                }

                if (tatalDisbursement.compareTo(totalPrincipal) == 1) {
                    baseDataValidator.reset().parameter(LoanApiConstants.disbursementPrincipalParameterName)
                            .failWithCode(LoanApiConstants.APPROVED_AMOUNT_IS_LESS_THAN_SUM_OF_TRANCHES);
                }
                final String interestTypeParameterName = "interestType";
                final Integer interestType = this.fromApiJsonHelper
                        .extractIntegerSansLocaleNamed(interestTypeParameterName, element);
                baseDataValidator.reset().parameter(interestTypeParameterName).value(interestType).ignoreIfNull()
                        .integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());

            }

        }

    }

    public void validateLoanCharges(final Set<LoanCharge> charges,
            final List<ApiParameterError> dataValidationErrors) {
        if (charges == null) {
            return;
        }
        final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
                .resource("loan");
        for (LoanCharge loanCharge : charges) {
            String errorcode = null;
            switch (loanCharge.getChargeCalculation()) {
            case PERCENT_OF_AMOUNT:
                if (loanCharge.isInstalmentFee()) {
                    errorcode = "installment."
                            + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE;

                }
                break;
            case PERCENT_OF_AMOUNT_AND_INTEREST:
                if (loanCharge.isInstalmentFee()) {
                    errorcode = "installment."
                            + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE;
                } else if (loanCharge.isSpecifiedDueDate()) {
                    errorcode = "specific."
                            + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE;
                }
                break;
            case PERCENT_OF_INTEREST:
                if (loanCharge.isSpecifiedDueDate()) {
                    errorcode = "specific."
                            + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE;
                }
                break;

            default:
                break;
            }
            if (errorcode != null) {
                baseDataValidator.reset().parameter("charges").failWithCode(errorcode);
            }
        }
    }

    public void validateLoanForInterestRecalculation(final Loan loan) {
        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();

        validateLoanCharges(loan.charges(), dataValidationErrors);
        if (!dataValidationErrors.isEmpty()) {
            throw new PlatformApiDataValidationException(dataValidationErrors);
        }
    }

    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()
                    ? interestCalculationPeriodMethod.isDaily()
                    : loanProduct.getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalcualtion();
            if (this.fromApiJsonHelper.parameterExists(
                    LoanProductConstants.allowPartialPeriodInterestCalcualtionParamName, element)) {
                final Boolean considerPartialInterestEnabled = this.fromApiJsonHelper.extractBooleanNamed(
                        LoanProductConstants.allowPartialPeriodInterestCalcualtionParamName, element);
                baseDataValidator.reset()
                        .parameter(LoanProductConstants.allowPartialPeriodInterestCalcualtionParamName)
                        .value(considerPartialInterestEnabled).notNull().isOneOfTheseValues(true, false);
                boolean considerPartialPeriods = considerPartialInterestEnabled == null ? false
                        : considerPartialInterestEnabled;
                if (interestCalculationPeriodMethod.isDaily()) {
                    if (considerPartialPeriods) {
                        baseDataValidator.reset()
                                .parameter(LoanProductConstants.allowPartialPeriodInterestCalcualtionParamName)
                                .failWithCode("not.supported.for.daily.calcualtions");
                    }
                } else {
                    considerPartialPeriodUpdates = considerPartialPeriods;
                }
            }

            if (!considerPartialPeriodUpdates) {
                if (loanProduct.isInterestRecalculationEnabled()) {
                    baseDataValidator.reset()
                            .parameter(LoanProductConstants.isInterestRecalculationEnabledParameterName)
                            .failWithCode("not.supported.for.selected.interest.calcualtion.type");
                }

                if (loanProduct.isMultiDisburseLoan()) {
                    baseDataValidator.reset().parameter(LoanProductConstants.multiDisburseLoanParameterName)
                            .failWithCode("not.supported.for.selected.interest.calcualtion.type");
                }

                if (loanProduct.allowVariabeInstallments()) {
                    baseDataValidator.reset().parameter(LoanProductConstants.allowVariableInstallmentsParamName)
                            .failWithCode("not.supported.for.selected.interest.calcualtion.type");
                }

                if (loanProduct.isLinkedToFloatingInterestRate()) {
                    baseDataValidator.reset().parameter("isLinkedToFloatingInterestRates")
                            .failWithCode("not.supported.for.selected.interest.calcualtion.type");
                }
            }

        }
    }

}