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