From 2418c09701750d655af4436cbc91ebcd284b7bd2 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Fri, 7 Nov 2014 13:15:27 +0530 Subject: [PATCH 1/3] MIFOSX-1641: Added capture of guarantee details for loan product and added constrain on savings withdraw --- .../domain/AccountAssociationType.java | 60 ++++++ .../account/domain/AccountAssociations.java | 21 ++- ...ccountAssociationsReadPlatformService.java | 9 +- ...ntAssociationsReadPlatformServiceImpl.java | 34 +++- .../loanaccount/api/LoansApiResource.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 6 +- ...WritePlatformServiceJpaRepositoryImpl.java | 16 +- .../loanproduct/LoanProductConstants.java | 6 + .../loanproduct/data/LoanProductData.java | 27 ++- .../data/LoanProductGuaranteeData.java | 41 ++++ .../loanproduct/domain/LoanProduct.java | 64 ++++++- .../domain/LoanProductGuaranteeDetails.java | 111 +++++++++++ .../LoanProductDataValidator.java | 88 ++++++++- .../LoanProductReadPlatformServiceImpl.java | 21 ++- ...WritePlatformServiceJpaRepositoryImpl.java | 4 +- .../api/FixedDepositAccountsApiResource.java | 2 +- .../savings/domain/SavingsAccount.java | 57 +++--- .../domain/SavingsAccountAssembler.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 178 +++++++++--------- .../V213__guarantee_on_hold_fund_changes.sql | 18 ++ 21 files changed, 602 insertions(+), 171 deletions(-) create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountAssociationType.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductGuaranteeData.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductGuaranteeDetails.java create mode 100755 mifosng-provider/src/main/resources/sql/migrations/core_db/V213__guarantee_on_hold_fund_changes.sql diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountAssociationType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountAssociationType.java new file mode 100755 index 000000000..0db4ced99 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountAssociationType.java @@ -0,0 +1,60 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.account.domain; + +/** + * Enum representation of loan status states. + */ +public enum AccountAssociationType { + + INVALID(0, "accountAssociationType.invalid"), // + LINKED_ACCOUNT_ASSOCIATION(1, "accountAssociationType.loan.account.association"), // + GUARANTOR_ACCOUNT_ASSOCIATION(2, "accountAssociationType.guarantor.account.association"); // + + private final Integer value; + private final String code; + + public static AccountAssociationType fromInt(final Integer statusValue) { + + AccountAssociationType enumeration = AccountAssociationType.INVALID; + switch (statusValue) { + case 1: + enumeration = AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION; + break; + case 2: + enumeration = AccountAssociationType.GUARANTOR_ACCOUNT_ASSOCIATION; + break; + + } + return enumeration; + } + + private AccountAssociationType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public boolean hasStateOf(final AccountAssociationType state) { + return this.value.equals(state.getValue()); + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isLinkedAccountAssociation() { + return this.value.equals(AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue()); + } + + public boolean isGuarantorAccountAssociation() { + return this.value.equals(AccountAssociationType.GUARANTOR_ACCOUNT_ASSOCIATION.getValue()); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountAssociations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountAssociations.java index ab1e791ed..0adb1e575 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountAssociations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountAssociations.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.account.domain; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; @@ -18,14 +19,17 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_portfolio_account_associations") public class AccountAssociations extends AbstractPersistable { + @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "loan_account_id", nullable = true) private Loan loanAccount; + @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "savings_account_id", nullable = true) private SavingsAccount savingsAccount; + @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "linked_loan_account_id", nullable = true) private Loan linkedLoanAccount; @@ -34,22 +38,29 @@ public class AccountAssociations extends AbstractPersistable { @JoinColumn(name = "linked_savings_account_id", nullable = true) private SavingsAccount linkedSavingsAccount; + @SuppressWarnings("unused") + @Column(name = "association_type_enum", nullable = false) + private Integer associationType; + protected AccountAssociations() {} private AccountAssociations(final Loan loanAccount, final SavingsAccount savingsAccount, final Loan linkedLoanAccount, - final SavingsAccount linkedSavingsAccount) { + final SavingsAccount linkedSavingsAccount, final Integer associationType) { this.loanAccount = loanAccount; this.savingsAccount = savingsAccount; this.linkedLoanAccount = linkedLoanAccount; this.linkedSavingsAccount = linkedSavingsAccount; + this.associationType = associationType; } - public static AccountAssociations associateSavingsAccount(final Loan loan, final SavingsAccount savingsAccount) { - return new AccountAssociations(loan, null, null, savingsAccount); + public static AccountAssociations associateSavingsAccount(final Loan loan, final SavingsAccount savingsAccount, + final Integer associationType) { + return new AccountAssociations(loan, null, null, savingsAccount, associationType); } - public static AccountAssociations associateSavingsAccount(final SavingsAccount savingsAccount, final SavingsAccount linkedSavingsAccount) { - return new AccountAssociations(null, savingsAccount, null, linkedSavingsAccount); + public static AccountAssociations associateSavingsAccount(final SavingsAccount savingsAccount, + final SavingsAccount linkedSavingsAccount, final Integer associationType) { + return new AccountAssociations(null, savingsAccount, null, linkedSavingsAccount, associationType); } public SavingsAccount linkedSavingsAccount() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountAssociationsReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountAssociationsReadPlatformService.java index 6e9ff83c6..2de655dce 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountAssociationsReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountAssociationsReadPlatformService.java @@ -5,13 +5,18 @@ */ package org.mifosplatform.portfolio.account.service; +import java.util.Collection; + +import org.mifosplatform.portfolio.account.data.AccountAssociationsData; import org.mifosplatform.portfolio.account.data.PortfolioAccountData; public interface AccountAssociationsReadPlatformService { - public PortfolioAccountData retriveLoanAssociation(final Long loanId); + public PortfolioAccountData retriveLoanLinkedAssociation(final Long loanId); public boolean isLinkedWithAnyActiveAccount(final Long savingsId); - public PortfolioAccountData retriveSavingsAssociation(final Long savingsId); + public PortfolioAccountData retriveSavingsLinkedAssociation(final Long savingsId); + + public Collection retriveLoanAssociations(Long loanId, Integer associationType); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountAssociationsReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountAssociationsReadPlatformServiceImpl.java index e7fc10d2c..e205be363 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountAssociationsReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountAssociationsReadPlatformServiceImpl.java @@ -7,12 +7,14 @@ package org.mifosplatform.portfolio.account.service; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collection; import java.util.List; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.portfolio.account.data.AccountAssociationsData; import org.mifosplatform.portfolio.account.data.PortfolioAccountData; +import org.mifosplatform.portfolio.account.domain.AccountAssociationType; import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; import org.slf4j.Logger; @@ -35,12 +37,13 @@ public class AccountAssociationsReadPlatformServiceImpl implements AccountAssoci } @Override - public PortfolioAccountData retriveLoanAssociation(final Long loanId) { + public PortfolioAccountData retriveLoanLinkedAssociation(final Long loanId) { PortfolioAccountData linkedAccount = null; final AccountAssociationsMapper mapper = new AccountAssociationsMapper(); - final String sql = "select " + mapper.schema() + " where aa.loan_account_id = ? "; + final String sql = "select " + mapper.schema() + " where aa.loan_account_id = ? and aa.association_type_enum = ?"; try { - final AccountAssociationsData accountAssociationsData = this.jdbcTemplate.queryForObject(sql, mapper, loanId); + final AccountAssociationsData accountAssociationsData = this.jdbcTemplate.queryForObject(sql, mapper, loanId, + AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue()); if (accountAssociationsData != null) { linkedAccount = accountAssociationsData.linkedAccount(); } @@ -49,14 +52,27 @@ public class AccountAssociationsReadPlatformServiceImpl implements AccountAssoci } return linkedAccount; } - + @Override - public PortfolioAccountData retriveSavingsAssociation(final Long savingsId) { + public Collection retriveLoanAssociations(final Long loanId, final Integer associationType) { + final AccountAssociationsMapper mapper = new AccountAssociationsMapper(); + final String sql = "select " + mapper.schema() + " where aa.loan_account_id = ? and aa.association_type_enum = ?"; + try { + return this.jdbcTemplate.query(sql, mapper, loanId, associationType); + } catch (final EmptyResultDataAccessException e) { + return null; + } + + } + + @Override + public PortfolioAccountData retriveSavingsLinkedAssociation(final Long savingsId) { PortfolioAccountData linkedAccount = null; final AccountAssociationsMapper mapper = new AccountAssociationsMapper(); - final String sql = "select " + mapper.schema() + " where aa.savings_account_id = ? "; + final String sql = "select " + mapper.schema() + " where aa.savings_account_id = ? and aa.association_type_enum = ?"; try { - final AccountAssociationsData accountAssociationsData = this.jdbcTemplate.queryForObject(sql, mapper, savingsId); + final AccountAssociationsData accountAssociationsData = this.jdbcTemplate.queryForObject(sql, mapper, savingsId, + AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue()); if (accountAssociationsData != null) { linkedAccount = accountAssociationsData.linkedAccount(); } @@ -74,7 +90,7 @@ public class AccountAssociationsReadPlatformServiceImpl implements AccountAssoci final List statusList = this.jdbcTemplate.queryForList(sql, Integer.class, savingsId); for (final Integer status : statusList) { - if(status !=null){ + if (status != null) { final LoanStatus loanStatus = LoanStatus.fromInt(status); if (loanStatus.isActiveOrAwaitingApprovalOrDisbursal() || loanStatus.isUnderTransfer()) { return true; } } @@ -86,7 +102,7 @@ public class AccountAssociationsReadPlatformServiceImpl implements AccountAssoci final List savstatusList = this.jdbcTemplate.queryForList(savsql, Integer.class, savingsId); for (final Integer status : savstatusList) { - if(status !=null){ + if (status != null) { final SavingsAccountStatusType saveStatus = SavingsAccountStatusType.fromInt(status); if (saveStatus.isActiveOrAwaitingApprovalOrDisbursal() || saveStatus.isUnderTransfer()) { return true; } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java index 2254fd10b..11b00829a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java @@ -405,7 +405,7 @@ public class LoansApiResource { if (associationParameters.contains("linkedAccount")) { mandatoryResponseParameters.add("linkedAccount"); - linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); + linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); } } @@ -460,7 +460,7 @@ public class LoansApiResource { if (!associationParameters.contains("linkedAccount")) { mandatoryResponseParameters.add("linkedAccount"); - linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); + linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); } if (loanBasicDetails.groupId() != null) { calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.groupId()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index f77e17cd3..1e42534c9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -26,6 +26,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityExce import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.account.domain.AccountAssociationType; import org.mifosplatform.portfolio.account.domain.AccountAssociations; import org.mifosplatform.portfolio.account.domain.AccountAssociationsRepository; import org.mifosplatform.portfolio.accountdetails.domain.AccountType; @@ -254,7 +255,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final SavingsAccount savingsAccount = this.savingsAccountAssembler.assembleFrom(savingsAccountId); this.fromApiJsonDeserializer.validatelinkedSavingsAccount(savingsAccount, newLoanApplication); final AccountAssociations accountAssociations = AccountAssociations.associateSavingsAccount(newLoanApplication, - savingsAccount); + savingsAccount, AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue()); this.accountAssociationsRepository.save(accountAssociations); } @@ -589,7 +590,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final SavingsAccount savingsAccount = this.savingsAccountAssembler.assembleFrom(savingsAccountId); this.fromApiJsonDeserializer.validatelinkedSavingsAccount(savingsAccount, existingLoanApplication); if (accountAssociations == null) { - accountAssociations = AccountAssociations.associateSavingsAccount(existingLoanApplication, savingsAccount); + accountAssociations = AccountAssociations.associateSavingsAccount(existingLoanApplication, savingsAccount, + AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue()); } else { accountAssociations.updateLinkedSavingsAccount(savingsAccount); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index eeb1b395a..7b41010db 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -361,7 +361,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); for (final Map.Entry entrySet : disBuLoanCharges.entrySet()) { - final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); + final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(), @@ -560,7 +560,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); for (final Map.Entry entrySet : disBuLoanCharges.entrySet()) { - final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loan + final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loan .getId()); final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; @@ -1312,7 +1312,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { - final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loan + final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loan .getId()); if (portfolioAccountData == null) { final String errorMessage = loanCharge.name() + "Charge requires linked savings account for payment"; @@ -1579,7 +1579,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf amount = chargePerInstallment.getAmountOutstanding(); } - final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); + final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); if (portfolioAccountData == null) { final String errorMessage = "Charge with id:" + loanChargeId + " requires linked savings account for payment"; throw new LinkedAccountRequiredException("loanCharge.pay", errorMessage, loanChargeId); @@ -1609,7 +1609,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); - final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loan.getId()); + final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loan.getId()); if (portfolioAccountData == null) { final String errorMessage = "Disburse Loan with id:" + loan.getId() + " requires linked savings account for payment"; throw new LinkedAccountRequiredException("loan.disburse.to.savings", errorMessage, loan.getId()); @@ -1641,7 +1641,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf for (final LoanInstallmentChargeData installmentChargeData : chargePerInstallments) { if (!installmentChargeData.getDueDate().isAfter(new LocalDate())) { if (portfolioAccountData == null) { - portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(chargeData + portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(chargeData .getLoanId()); } final SavingsAccount fromSavingsAccount = null; @@ -1656,7 +1656,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } else if (chargeData.getDueDate() != null && !chargeData.getDueDate().isAfter(new LocalDate())) { final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService - .retriveLoanAssociation(chargeData.getLoanId()); + .retriveLoanLinkedAssociation(chargeData.getLoanId()); final SavingsAccount fromSavingsAccount = null; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), chargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, @@ -2469,7 +2469,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf amount = chargePerInstallment.getAmountOutstanding(); } - final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); + final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); if (portfolioAccountData == null) { final String errorMessage = "Charge with id:" + loanChargeId + " requires linked savings account for payment"; throw new LinkedAccountRequiredException("loanCharge.pay", errorMessage, loanChargeId); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java index ee172aeb3..59a85aa99 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java @@ -72,4 +72,10 @@ public interface LoanProductConstants { public static final String recalculationRestFrequencyIntervalParameterName = "recalculationRestFrequencyInterval"; public static final String recalculationRestFrequencyDateParamName = "recalculationRestFrequencyDate"; + // Guarantee related + public static final String holdGuaranteeFundsParamName = "holdGuaranteeFunds"; + public static final String mandatoryGuaranteeParamName = "mandatoryGuarantee"; + public static final String minimumGuaranteeFromOwnFundsParamName = "minimumGuaranteeFromOwnFunds"; + public static final String minimumGuaranteeFromGuarantorParamName = "minimumGuaranteeFromGuarantor"; + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java index 8726cc806..8d2d27f1c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java @@ -135,6 +135,9 @@ public class LoanProductData { private final Integer maxTrancheCount; private final BigDecimal outstandingLoanBalance; + private final Boolean holdGuaranteeFunds; + private final LoanProductGuaranteeData productGuaranteeData; + /** * Used when returning lookup information about loan product for dropdowns. */ @@ -182,6 +185,8 @@ public class LoanProductData { final Boolean multiDisburseLoan = null; final Integer maxTrancheCount = null; final BigDecimal outstandingLoanBalance = null; + final LoanProductGuaranteeData productGuaranteeData = null; + final Boolean holdGuaranteeFunds = false; final EnumOptionData daysInMonthType = null; final EnumOptionData daysInYearType = null; @@ -197,7 +202,7 @@ public class LoanProductData { accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations, interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, - interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment); + interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData); } public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) { @@ -251,6 +256,8 @@ public class LoanProductData { final boolean isInterestRecalculationEnabled = false; final LoanProductInterestRecalculationData interestRecalculationData = null; final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null; + final Boolean holdGuaranteeFunds = false; + final LoanProductGuaranteeData productGuaranteeData = null; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -260,7 +267,7 @@ public class LoanProductData { accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations, interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, - interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment); + interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData); } public static LoanProductData sensibleDefaultsForNewLoanProductCreation() { @@ -321,7 +328,8 @@ public class LoanProductData { final LoanProductInterestRecalculationData interestRecalculationData = LoanProductInterestRecalculationData .sensibleDefaultsForNewLoanProductCreation(); final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null; - + final Boolean holdGuaranteeFunds = false; + final LoanProductGuaranteeData productGuaranteeData = null; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -331,7 +339,8 @@ public class LoanProductData { accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, - daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment); + daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, + holdGuaranteeFunds, productGuaranteeData); } public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map accountingMappings, @@ -362,7 +371,8 @@ public class LoanProductData { Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing, final Integer overdueDaysForNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData, - final Integer minimumDaysBetweenDisbursalAndFirstRepayment) { + final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds, + LoanProductGuaranteeData loanProductGuaranteeData) { this.id = id; this.name = name; this.shortName = shortName; @@ -433,12 +443,15 @@ public class LoanProductData { this.daysInYearType = daysInYearType; this.isInterestRecalculationEnabled = isInterestRecalculationEnabled; this.interestRecalculationData = interestRecalculationData; + this.holdGuaranteeFunds = holdGuaranteeFunds; + this.productGuaranteeData = loanProductGuaranteeData; this.daysInMonthTypeOptions = null; this.daysInYearTypeOptions = null; this.interestRecalculationCompoundingTypeOptions = null; this.rescheduleStrategyTypeOptions = null; this.interestRecalculationFrequencyTypeOptions = null; + } public LoanProductData(final LoanProductData productData, final Collection chargeOptions, @@ -515,7 +528,7 @@ public class LoanProductData { this.multiDisburseLoan = productData.multiDisburseLoan; this.maxTrancheCount = productData.maxTrancheCount; this.outstandingLoanBalance = productData.outstandingLoanBalance; - this.minimumDaysBetweenDisbursalAndFirstRepayment = productData.minimumDaysBetweenDisbursalAndFirstRepayment; + this.minimumDaysBetweenDisbursalAndFirstRepayment = productData.minimumDaysBetweenDisbursalAndFirstRepayment; this.amortizationTypeOptions = amortizationTypeOptions; this.interestTypeOptions = interestTypeOptions; @@ -533,6 +546,8 @@ public class LoanProductData { this.daysInYearType = productData.daysInYearType; this.isInterestRecalculationEnabled = productData.isInterestRecalculationEnabled; this.interestRecalculationData = productData.interestRecalculationData; + this.holdGuaranteeFunds = productData.holdGuaranteeFunds; + this.productGuaranteeData = productData.productGuaranteeData; this.daysInMonthTypeOptions = daysInMonthTypeOptions; this.daysInYearTypeOptions = daysInYearTypeOptions; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductGuaranteeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductGuaranteeData.java new file mode 100755 index 000000000..3e349559d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductGuaranteeData.java @@ -0,0 +1,41 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanproduct.data; + +import java.math.BigDecimal; + +public class LoanProductGuaranteeData { + + @SuppressWarnings("unused") + private final Long id; + @SuppressWarnings("unused") + private final Long productId; + @SuppressWarnings("unused") + private final BigDecimal mandatoryGuarantee; + @SuppressWarnings("unused") + private final BigDecimal minimumGuaranteeFromOwnFunds; + @SuppressWarnings("unused") + private final BigDecimal minimumGuaranteeFromGuarantor; + + public static LoanProductGuaranteeData instance(final Long id, final Long productId, final BigDecimal mandatoryGuarantee, + final BigDecimal minimumGuaranteeFromOwnFunds, final BigDecimal minimumGuaranteeFromGuarantor) { + return new LoanProductGuaranteeData(id, productId, mandatoryGuarantee, minimumGuaranteeFromOwnFunds, minimumGuaranteeFromGuarantor); + } + + public static LoanProductGuaranteeData sensibleDefaultsForNewLoanProductCreation() { + return new LoanProductGuaranteeData(null, null, null, null, null); + } + + private LoanProductGuaranteeData(final Long id, final Long productId, final BigDecimal mandatoryGuarantee, + final BigDecimal minimumGuaranteeFromOwnFunds, final BigDecimal minimumGuaranteeFromGuarantor) { + this.id = id; + this.productId = productId; + this.mandatoryGuarantee = mandatoryGuarantee; + this.minimumGuaranteeFromGuarantor = minimumGuaranteeFromGuarantor; + this.minimumGuaranteeFromOwnFunds = minimumGuaranteeFromOwnFunds; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 4e0bfa731..8f16132d2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -129,6 +129,13 @@ public class LoanProduct extends AbstractPersistable { @OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true) private LoanProductInterestRecalculationDetails productInterestRecalculationDetails; + @Column(name = "hold_guarantee_funds") + private boolean holdGuaranteeFunds; + + @LazyCollection(LazyCollectionOption.FALSE) + @OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true) + private LoanProductGuaranteeDetails loanProductGuaranteeDetails; + public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy, final List productCharges, final JsonCommand command, final AprCalculator aprCalculator) { @@ -211,6 +218,13 @@ public class LoanProduct extends AbstractPersistable { if (isInterestRecalculationEnabled) { interestRecalculationSettings = LoanProductInterestRecalculationDetails.createFrom(command); } + + final boolean holdGuarantorFunds = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.holdGuaranteeFundsParamName); + LoanProductGuaranteeDetails loanProductGuaranteeDetails = null; + if (holdGuarantorFunds) { + loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command); + } + return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType, @@ -218,12 +232,18 @@ public class LoanProduct extends AbstractPersistable { graceOnInterestCharged, amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate, externalId, useBorrowerCycle, loanProductBorrowerCycleVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, - isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment); + isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment, + holdGuarantorFunds, loanProductGuaranteeDetails); } - public void updateLoanProductForInterestRecalculationDetails() { - this.productInterestRecalculationDetails.updateProduct(this); + public void updateLoanProductInRelatedClasses() { + if (this.isInterestRecalculationEnabled()) { + this.productInterestRecalculationDetails.updateProduct(this); + } + if (this.holdGuaranteeFunds) { + this.loanProductGuaranteeDetails.updateProduct(this); + } } /** @@ -437,7 +457,8 @@ public class LoanProduct extends AbstractPersistable { final Integer overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationDetails productInterestRecalculationDetails, - final Integer minimumDaysBetweenDisbursalAndFirstRepayment) { + final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds, + final LoanProductGuaranteeDetails loanProductGuaranteeDetails) { this.fund = fund; this.transactionProcessingStrategy = transactionProcessingStrategy; this.name = name.trim(); @@ -487,6 +508,8 @@ public class LoanProduct extends AbstractPersistable { this.overdueDaysForNPA = overdueDaysForNPA; this.productInterestRecalculationDetails = productInterestRecalculationDetails; this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment; + this.holdGuaranteeFunds = holdGuarantorFunds; + this.loanProductGuaranteeDetails = loanProductGuaranteeDetails; } public MonetaryCurrency getCurrency() { @@ -690,6 +713,30 @@ public class LoanProduct extends AbstractPersistable { this.productInterestRecalculationDetails.update(command, actualChanges, localeAsInput); } + if (command.isChangeInBooleanParameterNamed(LoanProductConstants.holdGuaranteeFundsParamName, this.holdGuaranteeFunds)) { + final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.holdGuaranteeFundsParamName); + actualChanges.put(LoanProductConstants.holdGuaranteeFundsParamName, newValue); + this.holdGuaranteeFunds = newValue; + } + + if (actualChanges.containsKey(LoanProductConstants.holdGuaranteeFundsParamName)) { + if (this.holdGuaranteeFunds) { + this.loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command); + this.loanProductGuaranteeDetails.updateProduct(this); + actualChanges.put(LoanProductConstants.mandatoryGuaranteeParamName, + this.loanProductGuaranteeDetails.getMandatoryGuarantee()); + actualChanges.put(LoanProductConstants.minimumGuaranteeFromGuarantorParamName, + this.loanProductGuaranteeDetails.getMinimumGuaranteeFromGuarantor()); + actualChanges.put(LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, + this.loanProductGuaranteeDetails.getMinimumGuaranteeFromOwnFunds()); + } else { + this.loanProductGuaranteeDetails = null; + } + + } else if (this.holdGuaranteeFunds) { + this.loanProductGuaranteeDetails.update(command, actualChanges); + } + return actualChanges; } @@ -929,4 +976,13 @@ public class LoanProduct extends AbstractPersistable { public LoanProductInterestRecalculationDetails getProductInterestRecalculationDetails() { return this.productInterestRecalculationDetails; } + + public boolean isHoldGuaranteeFundsEnabled() { + return this.holdGuaranteeFunds; + } + + + public LoanProductGuaranteeDetails getLoanProductGuaranteeDetails() { + return this.loanProductGuaranteeDetails; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductGuaranteeDetails.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductGuaranteeDetails.java new file mode 100755 index 000000000..9dcf4efbc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductGuaranteeDetails.java @@ -0,0 +1,111 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanproduct.domain; + +import java.math.BigDecimal; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.portfolio.loanproduct.LoanProductConstants; +import org.springframework.data.jpa.domain.AbstractPersistable; + +/** + * Entity for capturing interest recalculation settings + * + * @author conflux + */ + +@Entity +@Table(name = "m_product_loan_guarantee_details") +public class LoanProductGuaranteeDetails extends AbstractPersistable { + + @SuppressWarnings("unused") + @OneToOne + @JoinColumn(name = "loan_product_id", nullable = false) + private LoanProduct loanProduct; + + @Column(name = "mandatory_guarantee", scale = 6, precision = 19, nullable = false) + private BigDecimal mandatoryGuarantee; + + @Column(name = "minimum_guarantee_from_own_funds", scale = 6, precision = 19, nullable = false) + private BigDecimal minimumGuaranteeFromOwnFunds; + + @Column(name = "minimum_guarantee_from_guarantor_funds", scale = 6, precision = 19, nullable = false) + private BigDecimal minimumGuaranteeFromGuarantor; + + protected LoanProductGuaranteeDetails() { + // + } + + public static LoanProductGuaranteeDetails createFrom(final JsonCommand command) { + + final BigDecimal mandatoryGuarantee = command.bigDecimalValueOfParameterNamed(LoanProductConstants.mandatoryGuaranteeParamName); + final BigDecimal minimumGuaranteeFromGuarantor = command + .bigDecimalValueOfParameterNamed(LoanProductConstants.minimumGuaranteeFromGuarantorParamName); + final BigDecimal minimumGuaranteeFromOwnFunds = command + .bigDecimalValueOfParameterNamed(LoanProductConstants.minimumGuaranteeFromOwnFundsParamName); + + return new LoanProductGuaranteeDetails(mandatoryGuarantee, minimumGuaranteeFromOwnFunds, minimumGuaranteeFromGuarantor); + } + + private LoanProductGuaranteeDetails(final BigDecimal mandatoryGuarantee, final BigDecimal minimumGuaranteeFromOwnFunds, + final BigDecimal minimumGuaranteeFromGuarantor) { + this.mandatoryGuarantee = mandatoryGuarantee; + this.minimumGuaranteeFromGuarantor = minimumGuaranteeFromGuarantor; + this.minimumGuaranteeFromOwnFunds = minimumGuaranteeFromOwnFunds; + } + + public void updateProduct(final LoanProduct loanProduct) { + this.loanProduct = loanProduct; + } + + public void update(final JsonCommand command, final Map actualChanges) { + + if (command.isChangeInBigDecimalParameterNamed(LoanProductConstants.mandatoryGuaranteeParamName, this.mandatoryGuarantee)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(LoanProductConstants.mandatoryGuaranteeParamName); + actualChanges.put(LoanProductConstants.mandatoryGuaranteeParamName, newValue); + this.mandatoryGuarantee = newValue; + } + + if (command.isChangeInBigDecimalParameterNamed(LoanProductConstants.minimumGuaranteeFromGuarantorParamName, + this.minimumGuaranteeFromGuarantor)) { + final BigDecimal newValue = command + .bigDecimalValueOfParameterNamed(LoanProductConstants.minimumGuaranteeFromGuarantorParamName); + actualChanges.put(LoanProductConstants.minimumGuaranteeFromGuarantorParamName, newValue); + this.minimumGuaranteeFromGuarantor = newValue; + } + + if (command.isChangeInBigDecimalParameterNamed(LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, + this.minimumGuaranteeFromOwnFunds)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(LoanProductConstants.minimumGuaranteeFromOwnFundsParamName); + actualChanges.put(LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, newValue); + this.minimumGuaranteeFromOwnFunds = newValue; + } + + } + + + public BigDecimal getMandatoryGuarantee() { + return this.mandatoryGuarantee; + } + + + public BigDecimal getMinimumGuaranteeFromOwnFunds() { + return this.minimumGuaranteeFromOwnFunds; + } + + + public BigDecimal getMinimumGuaranteeFromGuarantor() { + return this.minimumGuaranteeFromGuarantor; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java index 2505a5d85..9fd07e4fb 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -71,7 +71,9 @@ public final class LoanProductDataValidator { LoanProductConstants.recalculationRestFrequencyDateParamName, LoanProductConstants.recalculationRestFrequencyIntervalParameterName, LoanProductConstants.recalculationRestFrequencyTypeParameterName, - LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment)); + LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, LoanProductConstants.mandatoryGuaranteeParamName, + LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName, + LoanProductConstants.minimumGuaranteeFromOwnFundsParamName)); private final FromJsonHelper fromApiJsonHelper; @@ -309,6 +311,20 @@ public final class LoanProductDataValidator { } } + // 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); + } + } + // accounting related data validation final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault()); baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 4); @@ -704,16 +720,31 @@ public final class LoanProductDataValidator { } // Interest recalculation settings + Boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled(); if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.isInterestRecalculationEnabledParameterName, element)) { - final Boolean isInterestRecalculationEnabled = this.fromApiJsonHelper.extractBooleanNamed( + isInterestRecalculationEnabled = this.fromApiJsonHelper.extractBooleanNamed( LoanProductConstants.isInterestRecalculationEnabledParameterName, element); baseDataValidator.reset().parameter(LoanProductConstants.isInterestRecalculationEnabledParameterName) .value(isInterestRecalculationEnabled).notNull().isOneOfTheseValues(true, false); + } - if (isInterestRecalculationEnabled != null) { - if (isInterestRecalculationEnabled.booleanValue()) { - validateInterestRecalculationParams(element, baseDataValidator, loanProduct); - } + if (isInterestRecalculationEnabled != null) { + if (isInterestRecalculationEnabled) { + validateInterestRecalculationParams(element, baseDataValidator, loanProduct); + } + } + + // 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); } } @@ -1279,4 +1310,49 @@ public final class LoanProductDataValidator { } + 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)) == -1) { + baseDataValidator.parameter(LoanProductConstants.mandatoryGuaranteeParamName).failWithCode( + "must.be.greter.than.sum.of.min.funds"); + } + + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index 817fa475d..f6d36339c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -24,6 +24,7 @@ import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService; import org.mifosplatform.portfolio.common.service.CommonEnumerations; import org.mifosplatform.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; +import org.mifosplatform.portfolio.loanproduct.data.LoanProductGuaranteeData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductInterestRecalculationData; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductParamType; import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException; @@ -128,10 +129,14 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo + "lpr.id as lprId, lpr.product_id as productId, lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as rescheduleStrategy, " + "lpr.rest_frequency_type_enum as restFrequencyEnum, lpr.rest_frequency_interval as restFrequencyInterval, " + "lpr.rest_freqency_date as restFrequencyDate, " + + "lp.hold_guarantee_funds as holdGuaranteeFunds, " + + "lpg.id as lpgId, lpg.mandatory_guarantee as mandatoryGuarantee, " + + "lpg.minimum_guarantee_from_own_funds as minimumGuaranteeFromOwnFunds, lpg.minimum_guarantee_from_guarantor_funds as minimumGuaranteeFromGuarantor, " + "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId " + " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id " + " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id " + + " left join m_product_loan_guarantee_details lpg on lpg.loan_product_id=lp.id " + " left join ref_loan_transaction_processing_strategy ltps on ltps.id = lp.loan_transaction_strategy_id" + " join m_currency curr on curr.code = lp.currency_code"; } @@ -173,7 +178,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final Integer graceOnInterestCharged = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestCharged"); final Integer graceOnArrearsAgeing = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnArrearsAgeing"); final Integer overdueDaysForNPA = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "overdueDaysForNPA"); - final Integer minimumDaysBetweenDisbursalAndFirstRepayment = JdbcSupport.getInteger(rs,"minimumDaysBetweenDisbursalAndFirstRepayment"); + final Integer minimumDaysBetweenDisbursalAndFirstRepayment = JdbcSupport.getInteger(rs, + "minimumDaysBetweenDisbursalAndFirstRepayment"); final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType"); final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId); @@ -256,6 +262,17 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo restFrequencyDate); } + final boolean holdGuaranteeFunds = rs.getBoolean("holdGuaranteeFunds"); + LoanProductGuaranteeData loanProductGuaranteeData = null; + if (holdGuaranteeFunds) { + final Long lpgId = JdbcSupport.getLong(rs, "lpgId"); + final BigDecimal mandatoryGuarantee = rs.getBigDecimal("mandatoryGuarantee"); + final BigDecimal minimumGuaranteeFromOwnFunds = rs.getBigDecimal("minimumGuaranteeFromOwnFunds"); + final BigDecimal minimumGuaranteeFromGuarantor = rs.getBigDecimal("minimumGuaranteeFromGuarantor"); + loanProductGuaranteeData = LoanProductGuaranteeData.instance(lpgId, id, mandatoryGuarantee, minimumGuaranteeFromOwnFunds, + minimumGuaranteeFromGuarantor); + } + return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, @@ -265,7 +282,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo closeDate, status, externalId, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, - interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment); + interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java index c8bbd81d1..e8ee2d9da 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java @@ -92,9 +92,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command, this.aprCalculator); - if (loanproduct.isInterestRecalculationEnabled()) { - loanproduct.updateLoanProductForInterestRecalculationDetails(); - } + loanproduct.updateLoanProductInRelatedClasses(); this.loanProductRepository.save(loanproduct); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java index 02969d87c..b7cb007b3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java @@ -209,7 +209,7 @@ public class FixedDepositAccountsApiResource { if (associationParameters.contains(SavingsApiConstants.linkedAccount)) { mandatoryResponseParameters.add(SavingsApiConstants.linkedAccount); - linkedAccount = this.accountAssociationsReadPlatformService.retriveSavingsAssociation(accountId); + linkedAccount = this.accountAssociationsReadPlatformService.retriveSavingsLinkedAssociation(accountId); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 0676eebf0..2f9b14f2d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -249,6 +249,9 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "min_required_balance", scale = 6, precision = 19, nullable = true) private BigDecimal minRequiredBalance; + @Column(name = "on_hold_funds_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal onHoldFunds; + @Temporal(TemporalType.DATE) @Column(name = "start_interest_calculation_date") protected Date startInterestCalculationDate; @@ -854,6 +857,7 @@ public class SavingsAccount extends AbstractPersistable { public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount, final boolean isWithdrawBalance) { final List transactionsSortedByDate = retreiveListOfTransactions(); Money runningBalance = Money.zero(this.currency); + Money minRequiredBalance = minRequiredBalanceDerived(getCurrency()); for (final SavingsAccountTransaction transaction : transactionsSortedByDate) { if (transaction.isNotReversed() && transaction.isCredit()) { runningBalance = runningBalance.plus(transaction.getAmount(this.currency)); @@ -866,21 +870,11 @@ public class SavingsAccount extends AbstractPersistable { final BigDecimal withdrawalFee = null; // deal with potential minRequiredBalance and // enforceMinRequiredBalance - if (!isWithdrawBalance && this.minRequiredBalance != null && this.enforceMinRequiredBalance && transaction.isWithdrawal()) { - if (runningBalance.minus(this.minRequiredBalance).isLessThanZero()) { throw new InsufficientAccountBalanceException( + if (!isWithdrawBalance && transaction.isDebit()) { + if (runningBalance.minus(minRequiredBalance).isLessThanZero()) { throw new InsufficientAccountBalanceException( "transactionAmount", getAccountBalance(), withdrawalFee, transactionAmount); } } - if (runningBalance.isLessThanZero()) { - Money limit = runningBalance.zero(); - if (this.allowOverdraft) { - if (this.overdraftLimit != null) { - limit = limit.plus(this.overdraftLimit); - } - } - if (limit.plus(runningBalance).isLessThanZero()) { throw new InsufficientAccountBalanceException("transactionAmount", - getAccountBalance(), withdrawalFee, transactionAmount); } - } } } @@ -888,7 +882,7 @@ public class SavingsAccount extends AbstractPersistable { final List transactionsSortedByDate = retreiveListOfTransactions(); Money runningBalance = Money.zero(this.currency); - + Money minRequiredBalance = minRequiredBalanceDerived(getCurrency()); for (final SavingsAccountTransaction transaction : transactionsSortedByDate) { if (transaction.isNotReversed() && transaction.isCredit()) { runningBalance = runningBalance.plus(transaction.getAmount(this.currency)); @@ -896,24 +890,20 @@ public class SavingsAccount extends AbstractPersistable { runningBalance = runningBalance.minus(transaction.getAmount(this.currency)); } - if (runningBalance.isLessThanZero()) { - // - final List dataValidationErrors = new ArrayList<>(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(depositAccountType() - .resourceName() + transactionAction); - - if (this.allowOverdraft) { - Money limit = runningBalance.zero(); - if (this.overdraftLimit != null) { - limit = limit.plus(this.overdraftLimit); - } - if (limit.plus(runningBalance).isLessThanZero()) { + // enforceMinRequiredBalance + if (transaction.isDebit()) { + if (runningBalance.minus(minRequiredBalance).isLessThanZero()) { + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(depositAccountType().resourceName() + transactionAction); + if (this.allowOverdraft) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("results.in.balance.exceeding.overdraft.limit"); + } else { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("results.in.balance.going.negative"); } - } else { - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("results.in.balance.going.negative"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } } } @@ -2430,4 +2420,15 @@ public class SavingsAccount extends AbstractPersistable { } return savingsAccountCharge; } + + private Money minRequiredBalanceDerived(final MonetaryCurrency currency) { + Money minReqBalance = Money.of(currency, this.onHoldFunds); + if (this.enforceMinRequiredBalance) { + minReqBalance = minReqBalance.plus(this.minRequiredBalance); + } + if (this.allowOverdraft) { + minReqBalance = minReqBalance.minus(this.overdraftLimit); + } + return minReqBalance; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index a314afe50..f9ac2e9c5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -231,7 +231,7 @@ public class SavingsAccountAssembler { } BigDecimal minRequiredBalance = null; - if (command.parameterExists(overdraftLimitParamName)) { + if (command.parameterExists(minRequiredBalanceParamName)) { minRequiredBalance = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minRequiredBalanceParamName); } else { minRequiredBalance = product.minRequiredBalance(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index 5818596dc..3e68fe2de 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -196,7 +196,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo if (amountForDeposit.isGreaterThanZero()) { final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService - .retriveSavingsAssociation(savingsId); + .retriveSavingsLinkedAssociation(savingsId); if (portfolioAccountData == null) { final PaymentDetail paymentDetail = null; @@ -274,7 +274,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo Money amountForDeposit = account.activateWithBalance(); if (amountForDeposit.isGreaterThanZero()) { final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService - .retriveSavingsAssociation(savingsId); + .retriveSavingsLinkedAssociation(savingsId); if (portfolioAccountData == null) { this.depositAccountDomainService.handleRDDeposit(account, fmt, account.getActivationLocalDate(), amountForDeposit.getAmount(), null, isRegularTransaction); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java index 0143392a9..c2727ad9e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java @@ -33,6 +33,7 @@ import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; +import org.mifosplatform.portfolio.account.domain.AccountAssociationType; import org.mifosplatform.portfolio.account.domain.AccountAssociations; import org.mifosplatform.portfolio.account.domain.AccountAssociationsRepository; import org.mifosplatform.portfolio.calendar.domain.Calendar; @@ -115,8 +116,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final FixedDepositAccountRepository fixedDepositAccountRepository, final RecurringDepositAccountRepository recurringDepositAccountRepository, final AccountAssociationsRepository accountAssociationsRepository, final FromJsonHelper fromJsonHelper, - final CalendarInstanceRepository calendarInstanceRepository, - final ConfigurationDomainService configurationDomainService) { + final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService) { this.context = context; this.savingAccountRepository = savingAccountRepository; this.depositAccountAssembler = depositAccountAssembler; @@ -171,11 +171,10 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl try { this.depositAccountDataValidator.validateFixedDepositForSubmit(command.json()); final AppUser submittedBy = this.context.authenticatedUser(); - + final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService - .isSavingsInterestPostingAtCurrentPeriodEnd(); - final Integer financialYearBeginningMonth = this.configurationDomainService - .retrieveFinancialYearBeginningMonth(); + .isSavingsInterestPostingAtCurrentPeriodEnd(); + final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final FixedDepositAccount account = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(command, submittedBy, DepositAccountType.FIXED_DEPOSIT); @@ -183,8 +182,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final MathContext mc = MathContext.DECIMAL64; final boolean isPreMatureClosure = false; - account.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure, - isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); + account.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, + financialYearBeginningMonth); this.fixedDepositAccountRepository.save(account); if (account.isAccountNumberRequiresAutoGeneration()) { @@ -201,7 +200,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsAccountId, DepositAccountType.SAVINGS_DEPOSIT); this.depositAccountDataValidator.validatelinkedSavingsAccount(savingsAccount, account); - final AccountAssociations accountAssociations = AccountAssociations.associateSavingsAccount(account, savingsAccount); + final AccountAssociations accountAssociations = AccountAssociations.associateSavingsAccount(account, savingsAccount, + AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue()); this.accountAssociationsRepository.save(accountAssociations); } @@ -227,11 +227,10 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl try { this.depositAccountDataValidator.validateRecurringDepositForSubmit(command.json()); final AppUser submittedBy = this.context.authenticatedUser(); - + final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService - .isSavingsInterestPostingAtCurrentPeriodEnd(); - final Integer financialYearBeginningMonth = this.configurationDomainService - .retrieveFinancialYearBeginningMonth(); + .isSavingsInterestPostingAtCurrentPeriodEnd(); + final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(command, submittedBy, DepositAccountType.RECURRING_DEPOSIT); @@ -257,8 +256,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl frequency = frequency == -1 ? 1 : frequency; account.generateSchedule(frequencyType, frequency, calendar); final boolean isPreMatureClosure = false; - account.updateMaturityDateAndAmount(mc, isPreMatureClosure, - isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); + account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, + financialYearBeginningMonth); account.validateApplicableInterestRate(); this.savingAccountRepository.save(account); @@ -334,11 +333,10 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl public CommandProcessingResult modifyFDApplication(final Long accountId, final JsonCommand command) { try { this.depositAccountDataValidator.validateFixedDepositForUpdate(command.json()); - + final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService - .isSavingsInterestPostingAtCurrentPeriodEnd(); - final Integer financialYearBeginningMonth = this.configurationDomainService - .retrieveFinancialYearBeginningMonth(); + .isSavingsInterestPostingAtCurrentPeriodEnd(); + final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Map changes = new LinkedHashMap<>(20); @@ -353,79 +351,10 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final MathContext mc = MathContext.DECIMAL64; final boolean isPreMatureClosure = false; account.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure, - isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); + isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); this.savingAccountRepository.save(account); } - - return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(accountId) // - .withOfficeId(account.officeId()) // - .withClientId(account.clientId()) // - .withGroupId(account.groupId()) // - .withSavingsId(accountId) // - .with(changes) // - .build(); - } catch (final DataAccessException dve) { - handleDataIntegrityIssues(command, dve); - return new CommandProcessingResult(Long.valueOf(-1)); - } - } - - @Transactional - @Override - public CommandProcessingResult modifyRDApplication(final Long accountId, final JsonCommand command) { - try { - this.depositAccountDataValidator.validateRecurringDepositForUpdate(command.json()); - final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService - .isSavingsInterestPostingAtCurrentPeriodEnd(); - final Integer financialYearBeginningMonth = this.configurationDomainService - .retrieveFinancialYearBeginningMonth(); - - final Map changes = new LinkedHashMap<>(20); - - final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(accountId, - DepositAccountType.RECURRING_DEPOSIT); - checkClientOrGroupActive(account); - account.modifyApplication(command, changes); - account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), DepositAccountType.RECURRING_DEPOSIT.resourceName()); - - if (!changes.isEmpty()) { - updateFDAndRDCommonChanges(changes, command, account); - final MathContext mc = MathContext.DECIMAL64; - final CalendarInstance calendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( - accountId, CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue()); - final Calendar calendar = calendarInstance.getCalendar(); - final PeriodFrequencyType frequencyType = CalendarFrequencyType.from(CalendarUtils.getFrequency(calendar.getRecurrence())); - Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence()); - frequency = frequency == -1 ? 1 : frequency; - account.generateSchedule(frequencyType, frequency, calendar); - final boolean isPreMatureClosure = false; - account.updateMaturityDateAndAmount(mc, isPreMatureClosure, - isSavingsInterestPostingAtCurrentPeriodEnd, - financialYearBeginningMonth); - account.validateApplicableInterestRate(); - this.savingAccountRepository.save(account); - - } - - // update calendar details - if (!account.isCalendarInherited()) { - final LocalDate calendarStartDate = account.depositStartDate(); - final Integer frequencyType = command.integerValueSansLocaleOfParameterNamed(recurringFrequencyTypeParamName); - final PeriodFrequencyType periodFrequencyType = PeriodFrequencyType.fromInt(frequencyType); - final Integer frequency = command.integerValueSansLocaleOfParameterNamed(recurringFrequencyParamName); - final Integer repeatsOnDay = calendarStartDate.getDayOfWeek(); - - CalendarInstance calendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( - accountId, CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue()); - Calendar calendar = calendarInstance.getCalendar(); - calendar.updateRepeatingCalendar(calendarStartDate, CalendarFrequencyType.from(periodFrequencyType), frequency, - repeatsOnDay); - this.calendarInstanceRepository.save(calendarInstance); - } - boolean isLinkedAccRequired = command.booleanPrimitiveValueOfParameterNamed(transferInterestToSavingsParamName); // Save linked account information @@ -458,7 +387,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl DepositAccountType.SAVINGS_DEPOSIT); this.depositAccountDataValidator.validatelinkedSavingsAccount(savingsAccount, account); if (accountAssociations == null) { - accountAssociations = AccountAssociations.associateSavingsAccount(account, savingsAccount); + accountAssociations = AccountAssociations.associateSavingsAccount(account, savingsAccount, + AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue()); } else { accountAssociations.updateLinkedSavingsAccount(savingsAccount); } @@ -482,6 +412,74 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl } } + @Transactional + @Override + public CommandProcessingResult modifyRDApplication(final Long accountId, final JsonCommand command) { + try { + this.depositAccountDataValidator.validateRecurringDepositForUpdate(command.json()); + + final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService + .isSavingsInterestPostingAtCurrentPeriodEnd(); + final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); + + final Map changes = new LinkedHashMap<>(20); + + final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(accountId, + DepositAccountType.RECURRING_DEPOSIT); + checkClientOrGroupActive(account); + account.modifyApplication(command, changes); + account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), DepositAccountType.RECURRING_DEPOSIT.resourceName()); + + if (!changes.isEmpty()) { + updateFDAndRDCommonChanges(changes, command, account); + final MathContext mc = MathContext.DECIMAL64; + final CalendarInstance calendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( + accountId, CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue()); + final Calendar calendar = calendarInstance.getCalendar(); + final PeriodFrequencyType frequencyType = CalendarFrequencyType.from(CalendarUtils.getFrequency(calendar.getRecurrence())); + Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence()); + frequency = frequency == -1 ? 1 : frequency; + account.generateSchedule(frequencyType, frequency, calendar); + final boolean isPreMatureClosure = false; + account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, + financialYearBeginningMonth); + account.validateApplicableInterestRate(); + this.savingAccountRepository.save(account); + + } + + // update calendar details + if (!account.isCalendarInherited()) { + final LocalDate calendarStartDate = account.depositStartDate(); + final Integer frequencyType = command.integerValueSansLocaleOfParameterNamed(recurringFrequencyTypeParamName); + final PeriodFrequencyType periodFrequencyType = PeriodFrequencyType.fromInt(frequencyType); + final Integer frequency = command.integerValueSansLocaleOfParameterNamed(recurringFrequencyParamName); + final Integer repeatsOnDay = calendarStartDate.getDayOfWeek(); + + CalendarInstance calendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( + accountId, CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue()); + Calendar calendar = calendarInstance.getCalendar(); + calendar.updateRepeatingCalendar(calendarStartDate, CalendarFrequencyType.from(periodFrequencyType), frequency, + repeatsOnDay); + this.calendarInstanceRepository.save(calendarInstance); + } + + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(accountId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(accountId) // + .with(changes) // + .build(); + } catch (final DataAccessException dve) { + handleDataIntegrityIssues(command, dve); + return new CommandProcessingResult(Long.valueOf(-1)); + } + } + private void updateFDAndRDCommonChanges(final Map changes, final JsonCommand command, final SavingsAccount account) { if (changes.containsKey(SavingsApiConstants.clientIdParamName)) { diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V213__guarantee_on_hold_fund_changes.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V213__guarantee_on_hold_fund_changes.sql new file mode 100755 index 000000000..d286f346e --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V213__guarantee_on_hold_fund_changes.sql @@ -0,0 +1,18 @@ +ALTER TABLE `m_savings_account` + ADD COLUMN `on_hold_funds_derived` DECIMAL(19,6) NULL DEFAULT NULL; + +ALTER TABLE `m_portfolio_account_associations` + ADD COLUMN `association_type_enum` SMALLINT(1) NOT NULL DEFAULT '1'; + +CREATE TABLE `m_product_loan_guarantee_details` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `loan_product_id` BIGINT(20) NOT NULL, + `mandatory_guarantee` DECIMAL(19,5) NOT NULL, + `minimum_guarantee_from_own_funds` DECIMAL(19,5) NULL, + `minimum_guarantee_from_guarantor_funds` DECIMAL(19,5) NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_guarantee_details_loan_product` FOREIGN KEY (`loan_product_id`) REFERENCES `m_product_loan` (`id`) +); + +ALTER TABLE `m_product_loan` + ADD COLUMN `hold_guarantee_funds` TINYINT(1) NOT NULL DEFAULT '0'; \ No newline at end of file From 915f187bef81160a1cb75e2d12db4638db6c0cd0 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Fri, 7 Nov 2014 15:56:33 +0530 Subject: [PATCH 2/3] MIFOSX-1643 : modified charge payment to bypass running balance check --- .../ClientSavingsIntegrationTest.java | 5 +++-- .../portfolio/savings/domain/SavingsAccount.java | 4 ++-- .../savings/domain/SavingsAccountCharge.java | 16 ++++++++++++++++ .../domain/SavingsAccountChargePaidBy.java | 4 ++++ .../domain/SavingsAccountTransaction.java | 9 +++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientSavingsIntegrationTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientSavingsIntegrationTest.java index f008f8875..b4b514ff7 100755 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientSavingsIntegrationTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientSavingsIntegrationTest.java @@ -202,9 +202,10 @@ public class ClientSavingsIntegrationTest { Assert.assertNotNull(clientID); final String minBalanceForInterestCalculation = null; final String minRequiredBalance = "1500.0"; + final String openningBalance = "1600"; final String enforceMinRequiredBalance = "true"; final boolean allowOverdraft = false; - final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE, + final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, openningBalance , minBalanceForInterestCalculation, minRequiredBalance, enforceMinRequiredBalance, allowOverdraft); Assert.assertNotNull(savingsProductID); @@ -227,7 +228,7 @@ public class ClientSavingsIntegrationTest { SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap); HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId); - Float balance = new Float(MINIMUM_OPENING_BALANCE); + Float balance = new Float(openningBalance); Float chargeAmt = 100f; balance -= chargeAmt; assertEquals("Verifying opening Balance", balance, summary.get("accountBalance")); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 2f9b14f2d..684269842 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -870,7 +870,7 @@ public class SavingsAccount extends AbstractPersistable { final BigDecimal withdrawalFee = null; // deal with potential minRequiredBalance and // enforceMinRequiredBalance - if (!isWithdrawBalance && transaction.isDebit()) { + if (!isWithdrawBalance && transaction.canProcessBalanceCheck()) { if (runningBalance.minus(minRequiredBalance).isLessThanZero()) { throw new InsufficientAccountBalanceException( "transactionAmount", getAccountBalance(), withdrawalFee, transactionAmount); } } @@ -891,7 +891,7 @@ public class SavingsAccount extends AbstractPersistable { } // enforceMinRequiredBalance - if (transaction.isDebit()) { + if (transaction.canProcessBalanceCheck()) { if (runningBalance.minus(minRequiredBalance).isLessThanZero()) { final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java index c4024419b..746373155 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java @@ -40,6 +40,10 @@ import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeWithoutMandatoryFieldException; import org.springframework.data.jpa.domain.AbstractPersistable; +/** + * @author dv6 + * + */ @Entity @Table(name = "m_savings_account_charge") public class SavingsAccountCharge extends AbstractPersistable { @@ -823,4 +827,16 @@ public class SavingsAccountCharge extends AbstractPersistable { public boolean isNotActive() { return !isActive(); } + + /** + * This method is to identify the charges which can override the savings + * rules(for example if there is a minimum enforced balance of 1000 on + * savings account with account balance of 1000, still these charges can be + * collected as these charges are initiated by system and it can bring down + * the balance below the enforced minimum balance). + * + */ + public boolean canOverriteSavingAccountRules() { + return !(this.isSavingsActivation() || this.isWithdrawalFee()); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargePaidBy.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargePaidBy.java index f824721fb..4da54a6fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargePaidBy.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargePaidBy.java @@ -77,4 +77,8 @@ public class SavingsAccountChargePaidBy extends AbstractPersistable { public boolean isPenaltyCharge() { return (this.savingsAccountCharge == null) ? false : this.savingsAccountCharge.isPenaltyCharge(); } + + public boolean canOverriteSavingAccountRules() { + return (this.savingsAccountCharge == null) ? false : this.savingsAccountCharge.canOverriteSavingAccountRules(); + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index 23dc7280e..0a7126218 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -514,6 +514,15 @@ public final class SavingsAccountTransaction extends AbstractPersistable { return SavingsAccountTransactionType.fromInt(this.typeOf).isWaiveCharge(); } + private boolean canOverriteSavingAccountRules() { + final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy(); + return (isChargeTransaction() && chargePaidBy != null) ? chargePaidBy.canOverriteSavingAccountRules() : false; + } + + public boolean canProcessBalanceCheck() { + return isDebit() && !canOverriteSavingAccountRules(); + } + public boolean isFeeCharge() { final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy(); return (isPayCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false; From c3e66a6bff89d0e6516022735fbf1a80ec527832 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Fri, 7 Nov 2014 15:58:38 +0530 Subject: [PATCH 3/3] Corrected test cases --- .../FinancialActivityAccountsTest.java | 2 +- ...mDaysBetweenDisbursalAndFirstRepaymentTest.java | 4 +--- .../common/loans/LoanProductTestBuilder.java | 14 ++++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/FinancialActivityAccountsTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/FinancialActivityAccountsTest.java index 40e414c73..63d6dae1a 100755 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/FinancialActivityAccountsTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/FinancialActivityAccountsTest.java @@ -10,7 +10,7 @@ import static org.junit.Assert.assertEquals; import java.util.HashMap; import java.util.List; -import junit.framework.Assert; +import org.junit.Assert; import org.junit.After; import org.junit.Before; diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/MinimumDaysBetweenDisbursalAndFirstRepaymentTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/MinimumDaysBetweenDisbursalAndFirstRepaymentTest.java index fbeb274b5..5f1248e6d 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/MinimumDaysBetweenDisbursalAndFirstRepaymentTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/MinimumDaysBetweenDisbursalAndFirstRepaymentTest.java @@ -6,7 +6,6 @@ package org.mifosplatform.integrationtests; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import java.util.HashMap; import java.util.List; @@ -23,7 +22,6 @@ import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilde import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder; import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker; import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper; -import org.mifosplatform.integrationtests.common.savings.SavingsAccountHelper; import com.jayway.restassured.builder.RequestSpecBuilder; import com.jayway.restassured.builder.ResponseSpecBuilder; @@ -136,7 +134,7 @@ public class MinimumDaysBetweenDisbursalAndFirstRepaymentTest { List error = (List) this.loanTransactionHelper.createLoanAccount(loanApplicationJSON, CommonConstants.RESPONSE_ERROR); - assertEquals("validation.msg.loan.days.between.first.repayment.and.disbursal.are.less than.minimum.number.of.days.required", error + assertEquals("error.msg.loan.days.between.first.repayment.and.disbursal.are.less.than.minimum.allowed.numbers", error .get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); } diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java index 113a5da30..23fc74f10 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java @@ -42,7 +42,7 @@ public class LoanProductTestBuilder { public static final String RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS = "2"; public static final String RECALCULATION_STRATEGY_REDUCE_EMI_AMOUN = "3"; - public static final String RECALCULATION_COMPOUNDING_METHOD_NONE= "0"; + public static final String RECALCULATION_COMPOUNDING_METHOD_NONE = "0"; public static final String RECALCULATION_COMPOUNDING_METHOD_INTEREST = "1"; public static final String RECALCULATION_COMPOUNDING_METHOD_FEE = "2"; public static final String RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE = "3"; @@ -87,7 +87,7 @@ public class LoanProductTestBuilder { private String recalculationRestFrequencyType = "1"; private String recalculationRestFrequencyInterval = "0"; private String recalculationRestFrequencyDate = null; - private String minimumDaysBetweenDisbursalAndFirstRepayment = "0"; + private String minimumDaysBetweenDisbursalAndFirstRepayment = null; public String build(final String chargeId) { final HashMap map = new HashMap<>(); @@ -121,7 +121,9 @@ public class LoanProductTestBuilder { map.put("minPrincipal", this.minPrincipal); map.put("maxPrincipal", this.maxPrincipal); map.put("overdueDaysForNPA", this.overdueDaysForNPA); - map.put("minimumDaysBetweenDisbursalAndFirstRepayment", this.minimumDaysBetweenDisbursalAndFirstRepayment); + if (this.minimumDaysBetweenDisbursalAndFirstRepayment != null) { + map.put("minimumDaysBetweenDisbursalAndFirstRepayment", this.minimumDaysBetweenDisbursalAndFirstRepayment); + } if (multiDisburseLoan) { map.put("multiDisburseLoan", this.multiDisburseLoan); map.put("maxTrancheCount", this.maxTrancheCount); @@ -379,8 +381,8 @@ public class LoanProductTestBuilder { return this; } - public LoanProductTestBuilder withMinimumDaysBetweenDisbursalAndFirstRepayment(final String minimumDaysBetweenDisbursalAndFirstRepayment){ - this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment; - return this; + public LoanProductTestBuilder withMinimumDaysBetweenDisbursalAndFirstRepayment(final String minimumDaysBetweenDisbursalAndFirstRepayment) { + this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment; + return this; } }