diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 06003c97c..59c019429 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -11572,6 +11572,12 @@ An enumeration that indicates the type of transaction processing strategy to be
If Specified as true, arrears will be identified based on original schedule.
|
+
+ | allowAttributeOverrides |
+
+
+ Specifies if select attributes may be overridden for individual loan accounts.
+ |
@@ -12817,7 +12823,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
numberOfRepaymentVariationsForBorrowerCycle,
interestRateVariationsForBorrowerCycle,
multiDisburseLoan,maxTrancheCount,
- outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf
+ outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides
@@ -12920,6 +12926,16 @@ Request Body:
"maxValue":"20"
}
],
+"allowAttributeOverrides":
+ {amortizationType : true,
+ interestType : true,
+ transactionProcessingStrategyId : false,
+ interestCalculationPeriodType : true,
+ inArrearsTolerance : false,
+ repaymentEvery : true,
+ graceOnPrincipalAndInterestPayment : true,
+ graceOnArrearsAgeing : true},
+
"accountingRule":"2",
"name":"product 5",
"shortName":"prd5",
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientLoanIntegrationTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientLoanIntegrationTest.java
index bade7d93c..0279c4e5e 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientLoanIntegrationTest.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientLoanIntegrationTest.java
@@ -40,6 +40,7 @@ import org.mifosplatform.integrationtests.common.savings.SavingsAccountHelper;
import org.mifosplatform.integrationtests.common.savings.SavingsProductHelper;
import org.mifosplatform.integrationtests.common.savings.SavingsStatusChecker;
+import com.google.gson.JsonObject;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType;
@@ -4025,6 +4026,7 @@ public class ClientLoanIntegrationTest {
}
}
+
/***
* Test case to verify Mifos Style payment strategy
*/
@@ -4558,5 +4560,124 @@ public class ClientLoanIntegrationTest {
.withMinimumOpenningBalance(minOpenningBalance).build();
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
}
+
+ @Test
+ public void testLoanProductConfiguration() {
+ final String proposedAmount = "5000";
+ JsonObject loanProductConfigurationAsTrue = new JsonObject();
+ loanProductConfigurationAsTrue = this.createLoanProductConfigurationDetail(loanProductConfigurationAsTrue, true);
+ JsonObject loanProductConfigurationAsFalse = new JsonObject();
+ loanProductConfigurationAsFalse = this.createLoanProductConfigurationDetail(loanProductConfigurationAsFalse, false);
+
+ final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
+ Integer loanProductID = this.loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder()
+ .withAmortizationTypeAsEqualInstallments().withRepaymentTypeAsMonth().withRepaymentAfterEvery("1")
+ .withRepaymentStrategy(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY).withInterestTypeAsDecliningBalance()
+ .withInterestCalculationPeriodTypeAsDays().withInArrearsTolerance("10").withMoratorium("2", "3")
+ .withLoanProductConfiguration(loanProductConfigurationAsTrue).build(null));
+ System.out.println("-----------------------LOAN PRODUCT CREATED WITH ATTRIBUTE CONFIGURATION AS TRUE--------------------------"
+ + loanProductID);
+ Integer loanID = applyForLoanApplicationWithProductConfigurationAsTrue(clientID, loanProductID, proposedAmount);
+ System.out.println("------------------------LOAN CREATED WITH ID------------------------------" + loanID);
+
+ loanProductID = this.loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
+ .withRepaymentTypeAsMonth().withRepaymentAfterEvery("1")
+ .withRepaymentStrategy(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY).withInterestTypeAsDecliningBalance()
+ .withInterestCalculationPeriodTypeAsDays().withInArrearsTolerance("10").withMoratorium("2", "3")
+ .withLoanProductConfiguration(loanProductConfigurationAsFalse).build(null));
+ System.out.println("-------------------LOAN PRODUCT CREATED WITH ATTRIBUTE CONFIGURATION AS FALSE----------------------"
+ + loanProductID);
+ /*
+ * Try to override attribute values in loan account when attribute
+ * configurations are set to false at product level
+ */
+ loanID = applyForLoanApplicationWithProductConfigurationAsFalse(clientID, loanProductID, proposedAmount);
+ System.out.println("--------------------------LOAN CREATED WITH ID-------------------------" + loanID);
+ this.validateIfValuesAreNotOverridden(loanID, loanProductID);
+ }
+
+ private void validateIfValuesAreNotOverridden(Integer loanID, Integer loanProductID) {
+ assertEquals(
+ this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID, "amortizationType"),
+ this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID, "amortizationType"));
+ assertEquals(this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID, "interestType"),
+ this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID, "interestType"));
+ assertEquals(this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID,
+ "transactionProcessingStrategyId"), this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID,
+ "transactionProcessingStrategyId"));
+ assertEquals(this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID,
+ "interestCalculationPeriodType"), this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID,
+ "interestCalculationPeriodType"));
+ assertEquals(this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID,
+ "repaymentFrequencyType"), this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID,
+ "repaymentFrequencyType"));
+ assertEquals(this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID,
+ "graceOnPrincipalPayment"), this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID,
+ "graceOnPrincipalPayment"));
+ assertEquals(this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID,
+ "graceOnInterestPayment"), this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID,
+ "graceOnInterestPayment"));
+ assertEquals(
+ this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID, "inArrearsTolerance"),
+ this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID, "inArrearsTolerance"));
+ assertEquals(
+ this.loanTransactionHelper.getLoanProductDetail(this.requestSpec, this.responseSpec, loanProductID, "graceOnArrearsAgeing"),
+ this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID, "graceOnArrearsAgeing"));
+ }
+
+ private JsonObject createLoanProductConfigurationDetail(JsonObject loanProductConfiguration, Boolean bool) {
+ loanProductConfiguration.addProperty("amortizationType", bool);
+ loanProductConfiguration.addProperty("interestType", bool);
+ loanProductConfiguration.addProperty("transactionProcessingStrategyId", bool);
+ loanProductConfiguration.addProperty("interestCalculationPeriodType", bool);
+ loanProductConfiguration.addProperty("inArrearsTolerance", bool);
+ loanProductConfiguration.addProperty("repaymentEvery", bool);
+ loanProductConfiguration.addProperty("graceOnPrincipalAndInterestPayment", bool);
+ loanProductConfiguration.addProperty("graceOnArrearsAgeing", bool);
+ return loanProductConfiguration;
+ }
+
+ private Integer applyForLoanApplicationWithProductConfigurationAsTrue(final Integer clientID, final Integer loanProductID,
+ String principal) {
+ System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
+ final String loanApplicationJSON = new LoanApplicationTestBuilder() //
+ .withPrincipal(principal) //
+ .withRepaymentEveryAfter("1") //
+ .withLoanTermFrequency("4") //
+ .withLoanTermFrequencyAsMonths() //
+ .withNumberOfRepayments("4") //
+ .withRepaymentFrequencyTypeAsMonths() //
+ .withInterestRatePerPeriod("2") //
+ .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
+ .withExpectedDisbursementDate("1 March 2014") //
+ .withSubmittedOnDate("1 March 2014") //
+ .build(clientID.toString(), loanProductID.toString(), null);
+ return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
+ }
+
+ private Integer applyForLoanApplicationWithProductConfigurationAsFalse(final Integer clientID, final Integer loanProductID,
+ String principal) {
+ System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
+ final String loanApplicationJSON = new LoanApplicationTestBuilder()
+ //
+ .withPrincipal(principal)
+ //
+ .withRepaymentEveryAfter("2")
+ //
+ .withAmortizationTypeAsEqualPrincipalPayments().withRepaymentFrequencyTypeAsWeeks()
+ .withwithRepaymentStrategy(LoanProductTestBuilder.RBI_INDIA_STRATEGY).withInterestTypeAsFlatBalance()
+ .withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withPrincipalGrace("1").withInterestGrace("1")
+ .withLoanTermFrequency("4") //
+ .withLoanTermFrequencyAsMonths() //
+ .withNumberOfRepayments("4") //
+ .withRepaymentFrequencyTypeAsMonths() //
+ .withInterestRatePerPeriod("2") //
+ .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
+ .withExpectedDisbursementDate("1 March 2014") //
+ .withSubmittedOnDate("1 March 2014") //
+ .build(clientID.toString(), loanProductID.toString(), null);
+
+ return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java
index e90eb8853..d22c45dcc 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java
@@ -74,8 +74,9 @@ public class LoanApplicationTestBuilder {
map.put("expectedDisbursementDate", this.expectedDisbursmentDate);
map.put("submittedOnDate", this.submittedOnDate);
map.put("loanType", this.loanType);
- map.put("repaymentsStartingFromDate", this.repaymentsStartingFromDate);
-
+ if(repaymentsStartingFromDate != null){
+ map.put("repaymentsStartingFromDate", this.repaymentsStartingFromDate);
+ }
if (charges != null) {
map.put("charges", charges);
}
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 b1d095303..6f1b9ff5f 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
@@ -14,6 +14,7 @@ import org.mifosplatform.integrationtests.common.Utils;
import org.mifosplatform.integrationtests.common.accounting.Account;
import com.google.gson.Gson;
+import com.google.gson.JsonObject;
public class LoanProductTestBuilder {
@@ -93,7 +94,10 @@ public class LoanProductTestBuilder {
private String minimumGuaranteeFromOwnFunds = null;
private String minimumGuaranteeFromGuarantor = null;
private String isArrearsBasedOnOriginalSchedule = null;
-
+ private String graceOnPrincipalPayment = "1";
+ private String graceOnInterestPayment = "1";
+ private JsonObject allowAttributeOverrides = null;
+
public String build(final String chargeId) {
final HashMap map = new HashMap<>();
@@ -161,6 +165,11 @@ public class LoanProductTestBuilder {
map.put("minimumGuaranteeFromOwnFunds", this.minimumGuaranteeFromOwnFunds);
}
}
+ map.put("graceOnPrincipalPayment", graceOnPrincipalPayment);
+ map.put("graceOnInterestPayment", graceOnInterestPayment);
+ if(allowAttributeOverrides != null){
+ map.put("allowAttributeOverrides", this.allowAttributeOverrides);
+ }
return new Gson().toJson(map);
}
@@ -416,4 +425,14 @@ public class LoanProductTestBuilder {
return this;
}
-}
+ public LoanProductTestBuilder withMoratorium(String principal, String interest) {
+ this.graceOnPrincipalPayment = principal;
+ this.graceOnInterestPayment = interest;
+ return this;
+ }
+
+ public LoanProductTestBuilder withLoanProductConfiguration(JsonObject loanProductConfigurableAttributes) {
+ this.allowAttributeOverrides = loanProductConfigurableAttributes;
+ return this;
+ }
+}
\ No newline at end of file
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 b5737d282..2b3dbe8d5 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
@@ -452,6 +452,7 @@ public class LoansApiResource {
}
Collection productOptions = null;
+ LoanProductData product = null;
Collection loanTermFrequencyTypeOptions = null;
Collection repaymentFrequencyTypeOptions = null;
Collection repaymentStrategyOptions = null;
@@ -472,6 +473,8 @@ public class LoansApiResource {
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if (template) {
productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
+ product = this.loanProductReadPlatformService.retrieveLoanProduct(loanBasicDetails.loanProductId());
+ loanBasicDetails.setProduct(product);
loanTermFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
repaymentFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
interestRateFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
index bbc6ad1c0..e29612ce1 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
@@ -35,6 +35,7 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleDat
import org.mifosplatform.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
import org.mifosplatform.portfolio.note.data.NoteData;
import org.springframework.util.CollectionUtils;
@@ -162,7 +163,7 @@ public class LoanAccountData {
private final Collection emiAmountVariations;
- private final LoanProductData product;
+ private LoanProductData product;
private final Map memberVariations;
@@ -300,6 +301,7 @@ public class LoanAccountData {
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance);
+
}
/**
@@ -420,6 +422,7 @@ public class LoanAccountData {
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance);
+
}
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
@@ -566,6 +569,7 @@ public class LoanAccountData {
memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement,
paidInAdvance);
+
}
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
@@ -591,6 +595,7 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
+
}
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
@@ -783,6 +788,7 @@ public class LoanAccountData {
acc.memberVariations, product, acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA,
product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement, paidInAdvance);
+
}
/*
@@ -883,6 +889,10 @@ public class LoanAccountData {
final PortfolioAccountData linkedAccount, final Collection disbursementDetails,
final Collection emiAmountVariations, final Collection overdueCharges,
final PaidInAdvanceData paidInAdvance) {
+ LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
+ if (acc.product != null) {
+ loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes();
+ }
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
@@ -1001,6 +1011,7 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
+
}
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
@@ -1114,6 +1125,7 @@ public class LoanAccountData {
final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance) {
+
this.id = id;
this.accountNo = accountNo;
this.status = status;
@@ -1273,6 +1285,9 @@ public class LoanAccountData {
this.originalSchedule = originalSchedule;
this.createStandingInstructionAtDisbursement = createStandingInstructionAtDisbursement;
this.paidInAdvance = paidInAdvance;
+ if(this.product != null){
+ this.product.setloanProductConfigurableAttributes(product.getloanProductConfigurableAttributes());
+ }
}
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
@@ -1342,6 +1357,10 @@ public class LoanAccountData {
return this.product;
}
+ public void setProduct(LoanProductData product){
+ this.product = product;
+ }
+
public GroupGeneralData groupData() {
return this.group;
}
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 3f1b35cd0..b9e85e65d 100755
--- 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
@@ -232,6 +232,11 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
validateSubmittedOnDate(newLoanApplication);
final LoanProductRelatedDetail productRelatedDetail = newLoanApplication.repaymentScheduleDetail();
+
+ if(loanProduct.getLoanProductConfigurableAttributes() != null){
+ updateProductRelatedDetails(productRelatedDetail,newLoanApplication);
+ }
+
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(newLoanApplication.getTermFrequency(),
newLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
@@ -301,6 +306,43 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
}
+ private void updateProductRelatedDetails(LoanProductRelatedDetail productRelatedDetail,
+ Loan loan){
+ final Boolean amortization = loan.loanProduct().getLoanProductConfigurableAttributes().getAmortizationBoolean();
+ final Boolean arrearsTolerance = loan.loanProduct().getLoanProductConfigurableAttributes().getArrearsToleranceBoolean();
+ final Boolean graceOnArrearsAging = loan.loanProduct().getLoanProductConfigurableAttributes().getGraceOnArrearsAgingBoolean();
+ final Boolean interestCalcPeriod = loan.loanProduct().getLoanProductConfigurableAttributes().getInterestCalcPeriodBoolean();
+ final Boolean interestMethod = loan.loanProduct().getLoanProductConfigurableAttributes().getInterestMethodBoolean();
+ final Boolean graceOnPrincipalAndInterestPayment = loan.loanProduct().getLoanProductConfigurableAttributes().getGraceOnPrincipalAndInterestPaymentBoolean();
+ final Boolean repaymentEvery = loan.loanProduct().getLoanProductConfigurableAttributes().getRepaymentEveryBoolean();
+ final Boolean transactionProcessingStrategy = loan.loanProduct().getLoanProductConfigurableAttributes().getTransactionProcessingStrategyBoolean();
+
+ if(!amortization){
+ productRelatedDetail.setAmortizationMethod(loan.loanProduct().getLoanProductRelatedDetail().getAmortizationMethod());
+ }
+ if(!arrearsTolerance){
+ productRelatedDetail.setInArrearsTolerance(loan.loanProduct().getLoanProductRelatedDetail().getArrearsTolerance());
+ }
+ if(!graceOnArrearsAging){
+ productRelatedDetail.setGraceOnArrearsAgeing(loan.loanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing());
+ }
+ if(!interestCalcPeriod){
+ productRelatedDetail.setInterestCalculationPeriodMethod(loan.loanProduct().getLoanProductRelatedDetail().getInterestCalculationPeriodMethod());
+ }
+ if(!interestMethod){
+ productRelatedDetail.setInterestMethod(loan.loanProduct().getLoanProductRelatedDetail().getInterestMethod());
+ }
+ if(!graceOnPrincipalAndInterestPayment){
+ productRelatedDetail.setGraceOnInterestPayment(loan.loanProduct().getLoanProductRelatedDetail().getGraceOnInterestPayment());
+ productRelatedDetail.setGraceOnPrincipalPayment(loan.loanProduct().getLoanProductRelatedDetail().getGraceOnPrincipalPayment());
+ }
+ if(!repaymentEvery){
+ productRelatedDetail.setRepayEvery(loan.loanProduct().getLoanProductRelatedDetail().getRepayEvery());
+ }
+ if(!transactionProcessingStrategy){
+ loan.updateTransactionProcessingStrategy(loan.loanProduct().getRepaymentStrategy());
+ }
+ }
private void createAndPersistCalendarInstanceForInterestRecalculation(final Loan loan) {
LocalDate calendarStartDate = loan.loanInterestRecalculationDetails().getRestFrequencyLocalDate();
@@ -461,10 +503,10 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
final String productIdParamName = "productId";
- if (changes.containsKey(productIdParamName)) {
- final Long productId = command.longValueOfParameterNamed(productIdParamName);
- final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
- if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
+ if (changes.containsKey(productIdParamName)) {
+ final Long productId = command.longValueOfParameterNamed(productIdParamName);
+ final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
+ if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
existingLoanApplication.updateLoanProduct(loanProduct);
if (!changes.containsKey("interestRateFrequencyType")) {
@@ -494,6 +536,11 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateIsInterestRecalculationEnabled();
validateSubmittedOnDate(existingLoanApplication);
+ final LoanProductRelatedDetail productRelatedDetail = existingLoanApplication.repaymentScheduleDetail();
+ if(existingLoanApplication.loanProduct().getLoanProductConfigurableAttributes() != null){
+ updateProductRelatedDetails(productRelatedDetail,existingLoanApplication);
+ }
+
final String fundIdParamName = "fundId";
if (changes.containsKey(fundIdParamName)) {
final Long fundId = command.longValueOfParameterNamed(fundIdParamName);
@@ -546,7 +593,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateLoanCharges(possiblyModifedLoanCharges);
}
- final LoanProductRelatedDetail productRelatedDetail = existingLoanApplication.repaymentScheduleDetail();
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(),
existingLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
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 ee44461c5..6729d050f 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
@@ -88,5 +88,15 @@ public interface LoanProductConstants {
// Fixed installment configuration related
public static final String canDefineEmiAmountParamName = "canDefineInstallmentAmount";
public static final String installmentAmountInMultiplesOfParamName = "installmentAmountInMultiplesOf";
+
+ //Loan Configurable Attributes
+ public static final String allowAttributeOverridesParamName = "allowAttributeOverrides";
+ public static final String amortizationTypeParamName = "amortizationType";
+ public static final String interestTypeParamName = "interestType";
+ public static final String transactionProcessingStrategyIdParamName = "transactionProcessingStrategyId";
+ public static final String interestCalculationPeriodTypeParamName = "interestCalculationPeriodType";
+ public static final String inArrearsToleranceParamName = "inArrearsTolerance";
+ public static final String repaymentEveryParamName = "repaymentEvery";
+ public static final String graceOnPrincipalAndInterestPaymentParamName = "graceOnPrincipalAndInterestPayment";
}
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 d21a27011..05952869f 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
@@ -30,6 +30,7 @@ import org.mifosplatform.portfolio.loanaccount.data.LoanInterestRecalculationDat
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.paymenttype.data.PaymentTypeData;
import org.springframework.util.CollectionUtils;
@@ -141,6 +142,7 @@ public class LoanProductData {
private final Boolean holdGuaranteeFunds;
private final LoanProductGuaranteeData productGuaranteeData;
private final Boolean accountMovesOutOfNPAOnlyOnArrearsCompletion;
+ private LoanProductConfigurableAttributes allowAttributeOverrides;
/**
* Used when returning lookup information about loan product for dropdowns.
@@ -201,6 +203,7 @@ public class LoanProductData {
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
final boolean canDefineInstallmentAmount = false;
final Integer installmentAmountInMultiplesOf = null;
+ final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -212,7 +215,8 @@ public class LoanProductData {
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
- installmentAmountInMultiplesOf);
+ installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
+
}
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
@@ -272,6 +276,7 @@ public class LoanProductData {
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false;
final boolean canDefineInstallmentAmount = false;
final Integer installmentAmountInMultiplesOf = null;
+ final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -283,7 +288,8 @@ public class LoanProductData {
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
- installmentAmountInMultiplesOf);
+ installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
+
}
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
@@ -350,6 +356,7 @@ public class LoanProductData {
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false;
final boolean canDefineInstallmentAmount = false;
final Integer installmentAmountInMultiplesOf = null;
+ final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -361,7 +368,8 @@ public class LoanProductData {
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType,
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment,
holdGuaranteeFunds, productGuaranteeData, principalThresholdForLastInstallment,
- accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf);
+ accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
+
}
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map accountingMappings,
@@ -395,7 +403,7 @@ public class LoanProductData {
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds,
final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstallment,
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean canDefineInstallmentAmount,
- Integer installmentAmountInMultiplesOf) {
+ Integer installmentAmountInMultiplesOf, LoanProductConfigurableAttributes allowAttributeOverrides) {
this.id = id;
this.name = name;
this.shortName = shortName;
@@ -470,6 +478,7 @@ public class LoanProductData {
this.productGuaranteeData = loanProductGuaranteeData;
this.principalThresholdForLastInstallment = principalThresholdForLastInstallment;
this.accountMovesOutOfNPAOnlyOnArrearsCompletion = accountMovesOutOfNPAOnlyOnArrearsCompletion;
+ this.allowAttributeOverrides = allowAttributeOverrides;
this.daysInMonthTypeOptions = null;
this.daysInYearTypeOptions = null;
@@ -583,6 +592,7 @@ public class LoanProductData {
this.daysInYearTypeOptions = daysInYearTypeOptions;
this.interestRecalculationCompoundingTypeOptions = interestRecalculationCompoundingTypeOptions;
this.rescheduleStrategyTypeOptions = rescheduleStrategyTypeOptions;
+ this.allowAttributeOverrides = productData.allowAttributeOverrides;
if (CollectionUtils.isEmpty(interestRecalculationFrequencyTypeOptions)) {
this.interestRecalculationFrequencyTypeOptions = null;
@@ -845,4 +855,12 @@ public class LoanProductData {
public boolean canDefineInstallmentAmount() {
return this.canDefineInstallmentAmount;
}
+
+ public LoanProductConfigurableAttributes getloanProductConfigurableAttributes() {
+ return this.allowAttributeOverrides;
+ }
+
+ public void setloanProductConfigurableAttributes(LoanProductConfigurableAttributes loanProductConfigurableAttributes) {
+ this.allowAttributeOverrides = loanProductConfigurableAttributes;
+ }
}
\ No newline at end of file
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 69c7dc542..d9f31782e 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
@@ -46,6 +46,7 @@ import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.springframework.data.jpa.domain.AbstractPersistable;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@@ -135,6 +136,10 @@ public class LoanProduct extends AbstractPersistable {
@LazyCollection(LazyCollectionOption.FALSE)
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
private LoanProductGuaranteeDetails loanProductGuaranteeDetails;
+
+
+ @OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
+ private LoanProductConfigurableAttributes loanConfigurableAttributes;
@Column(name = "principal_threshold_for_last_installment", scale = 2, precision = 5, nullable = false)
private BigDecimal principalThresholdForLastInstallment;
@@ -236,6 +241,17 @@ public class LoanProduct extends AbstractPersistable {
if (holdGuarantorFunds) {
loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command);
}
+
+ LoanProductConfigurableAttributes loanConfigurableAttributes = null;
+ if (command
+ .parameterExists(LoanProductConstants.allowAttributeOverridesParamName)) {
+ loanConfigurableAttributes = LoanProductConfigurableAttributes
+ .createFrom(command);
+ } else {
+ loanConfigurableAttributes = LoanProductConfigurableAttributes
+ .populateDefaultsForConfigurableAttributes();
+ }
+
BigDecimal principalThresholdForLastInstallment = command
.bigDecimalValueOfParameterNamed(LoanProductConstants.principalThresholdForLastInstallmentParamName);
@@ -259,7 +275,7 @@ public class LoanProduct extends AbstractPersistable {
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment,
holdGuarantorFunds, loanProductGuaranteeDetails, principalThresholdForLastInstallment,
- accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineEmiAmount, installmentAmountInMultiplesOf);
+ accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineEmiAmount, installmentAmountInMultiplesOf, loanConfigurableAttributes);
}
@@ -486,7 +502,7 @@ public class LoanProduct extends AbstractPersistable {
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds,
final LoanProductGuaranteeDetails loanProductGuaranteeDetails, final BigDecimal principalThresholdForLastInstallment,
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, final boolean canDefineEmiAmount,
- final Integer installmentAmountInMultiplesOf) {
+ final Integer installmentAmountInMultiplesOf, final LoanProductConfigurableAttributes loanProductConfigurableAttributes) {
this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.name = name.trim();
@@ -532,6 +548,11 @@ public class LoanProduct extends AbstractPersistable {
for (LoanProductBorrowerCycleVariations borrowerCycleVariations : this.borrowerCycleVariations) {
borrowerCycleVariations.updateLoanProduct(this);
}
+ if (loanProductConfigurableAttributes != null) {
+ this.loanConfigurableAttributes = loanProductConfigurableAttributes;
+ loanConfigurableAttributes.updateLoanProduct(this);
+ }
+
this.loanProducTrancheDetails = new LoanProductTrancheDetails(multiDisburseLoan, maxTrancheCount, outstandingLoanBalance);
this.overdueDaysForNPA = overdueDaysForNPA;
this.productInterestRecalculationDetails = productInterestRecalculationDetails;
@@ -556,6 +577,10 @@ public class LoanProduct extends AbstractPersistable {
this.transactionProcessingStrategy = strategy;
}
+ public LoanTransactionProcessingStrategy getRepaymentStrategy(){
+ return this.transactionProcessingStrategy;
+ }
+
public boolean hasCurrencyCodeOf(final String currencyCode) {
return this.loanProductRelatedDetail.hasCurrencyCodeOf(currencyCode);
}
@@ -583,6 +608,13 @@ public class LoanProduct extends AbstractPersistable {
return this.accountingRule;
}
+ public void update(final LoanProductConfigurableAttributes loanConfigurableAttributes) {
+ this.loanConfigurableAttributes = loanConfigurableAttributes;
+ }
+
+ public LoanProductConfigurableAttributes getLoanProductConfigurableAttributes() {
+ return this.loanConfigurableAttributes;
+ }
public Map update(final JsonCommand command, final AprCalculator aprCalculator) {
final Map actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator);
@@ -751,6 +783,71 @@ public class LoanProduct extends AbstractPersistable {
this.holdGuaranteeFunds = newValue;
}
+ final String configurableAttributesChanges = LoanProductConstants.allowAttributeOverridesParamName;
+ if (command.hasParameter(configurableAttributesChanges)) {
+ if (!command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).isJsonNull()) {
+ actualChanges.put(configurableAttributesChanges, command.jsonFragment(configurableAttributesChanges));
+
+ if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.amortizationTypeParamName).getAsBoolean() != this.loanConfigurableAttributes.getAmortizationBoolean()) {
+ this.loanConfigurableAttributes.setAmortizationType(command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.amortizationTypeParamName).getAsBoolean());
+ }
+
+ if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.interestTypeParamName)
+ .getAsBoolean() != this.loanConfigurableAttributes.getInterestMethodBoolean()) {
+ this.loanConfigurableAttributes.setInterestType(command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.interestTypeParamName).getAsBoolean());
+ }
+
+ if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyIdParamName).getAsBoolean() != this.loanConfigurableAttributes
+ .getTransactionProcessingStrategyBoolean()) {
+ this.loanConfigurableAttributes.setTransactionProcessingStrategyId(command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyIdParamName)
+ .getAsBoolean());
+ }
+
+ if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.interestCalculationPeriodTypeParamName).getAsBoolean() != this.loanConfigurableAttributes
+ .getInterestCalcPeriodBoolean()) {
+ this.loanConfigurableAttributes.setInterestCalculationPeriodType(command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.interestCalculationPeriodTypeParamName).getAsBoolean());
+ }
+
+ if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.inArrearsToleranceParamName).getAsBoolean() != this.loanConfigurableAttributes
+ .getArrearsToleranceBoolean()) {
+ this.loanConfigurableAttributes.setInArrearsTolerance(command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.inArrearsToleranceParamName).getAsBoolean());
+ }
+
+ if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.repaymentEveryParamName)
+ .getAsBoolean() != this.loanConfigurableAttributes.getRepaymentEveryBoolean()) {
+ this.loanConfigurableAttributes.setRepaymentEvery(command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.repaymentEveryParamName).getAsBoolean());
+ }
+
+ if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.graceOnPrincipalAndInterestPaymentParamName).getAsBoolean() != this.loanConfigurableAttributes
+ .getGraceOnPrincipalAndInterestPaymentBoolean()) {
+ this.loanConfigurableAttributes.setGraceOnPrincipalAndInterestPayment(command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.graceOnPrincipalAndInterestPaymentParamName)
+ .getAsBoolean());
+ }
+
+ if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.graceOnArrearsAgeingParameterName).getAsBoolean() != this.loanConfigurableAttributes
+ .getGraceOnArrearsAgingBoolean()) {
+ this.loanConfigurableAttributes.setGraceOnArrearsAgeing(command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName).getAsJsonPrimitive(LoanProductConstants.graceOnArrearsAgeingParameterName).getAsBoolean());
+ }
+ } else {
+ this.loanConfigurableAttributes = LoanProductConfigurableAttributes.populateDefaultsForConfigurableAttributes();
+ this.loanConfigurableAttributes.updateLoanProduct(this);
+ }
+ }
+
if (actualChanges.containsKey(LoanProductConstants.holdGuaranteeFundsParamName)) {
if (this.holdGuaranteeFunds) {
this.loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command);
@@ -799,6 +896,7 @@ public class LoanProduct extends AbstractPersistable {
return actualChanges;
}
+
public boolean isAccountingDisabled() {
return AccountingRuleType.NONE.getValue().equals(this.accountingRule);
@@ -916,7 +1014,7 @@ public class LoanProduct extends AbstractPersistable {
public boolean isInterestRecalculationEnabled() {
return this.loanProductRelatedDetail.isInterestRecalculationEnabled();
}
-
+
public Integer getMinimumDaysBetweenDisbursalAndFirstRepayment() {
return this.minimumDaysBetweenDisbursalAndFirstRepayment == null ? 0 : this.minimumDaysBetweenDisbursalAndFirstRepayment;
}
@@ -1069,4 +1167,7 @@ public class LoanProduct extends AbstractPersistable {
return this.installmentAmountInMultiplesOf;
}
+ public LoanProductRelatedDetail getLoanProductRelatedDetail() {
+ return loanProductRelatedDetail;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductConfigurableAttributes.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductConfigurableAttributes.java
new file mode 100644
index 000000000..533509c51
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductConfigurableAttributes.java
@@ -0,0 +1,196 @@
+/**
+ * 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 javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+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
+@Table(name = "m_product_loan_configurable_attributes")
+public class LoanProductConfigurableAttributes extends AbstractPersistable {
+
+ @ManyToOne
+ @JoinColumn(name = "loan_product_id", nullable = true)
+ private LoanProduct loanProduct;
+
+ @Column(name = "amortization_method_enum", nullable = true)
+ private Boolean amortizationType;
+
+ @Column(name = "interest_method_enum", nullable = true)
+ private Boolean interestType;
+
+ @Column(name = "loan_transaction_strategy_id", nullable = true)
+ private Boolean transactionProcessingStrategyId;
+
+ @Column(name = "interest_calculated_in_period_enum", nullable = true)
+ private Boolean interestCalculationPeriodType;
+
+ @Column(name = "arrearstolerance_amount", nullable = true)
+ private Boolean inArrearsTolerance;
+
+ @Column(name = "repay_every", nullable = true)
+ private Boolean repaymentEvery;
+
+ @Column(name = "moratorium", nullable = true)
+ private Boolean graceOnPrincipalAndInterestPayment;
+
+ @Column(name = "grace_on_arrears_ageing", nullable = true)
+ private Boolean graceOnArrearsAgeing;
+
+ public static String[] supportedloanConfigurableAttributes = { "amortizationType", "interestType", "transactionProcessingStrategyId",
+ "interestCalculationPeriodType", "inArrearsTolerance", "repaymentEvery", "graceOnPrincipalAndInterestPayment",
+ "graceOnArrearsAging" };
+
+ public static LoanProductConfigurableAttributes createFrom(JsonCommand command) {
+
+ final Boolean amortization = command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.amortizationTypeParamName).getAsBoolean();
+ final Boolean interestMethod = command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.interestTypeParamName).getAsBoolean();
+ final Boolean transactionProcessingStrategy = command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyIdParamName).getAsBoolean();
+ final Boolean interestCalcPeriod = command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.interestCalculationPeriodTypeParamName).getAsBoolean();
+ final Boolean arrearsTolerance = command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.inArrearsToleranceParamName).getAsBoolean();
+ final Boolean repaymentEvery = command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.repaymentEveryParamName).getAsBoolean();
+ final Boolean graceOnPrincipalAndInterestPayment = command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.graceOnPrincipalAndInterestPaymentParamName).getAsBoolean();
+ final Boolean graceOnArrearsAging = command.parsedJson().getAsJsonObject()
+ .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
+ .getAsJsonPrimitive(LoanProductConstants.graceOnArrearsAgeingParameterName).getAsBoolean();
+
+ return new LoanProductConfigurableAttributes(amortization, interestMethod, transactionProcessingStrategy, interestCalcPeriod,
+ arrearsTolerance, repaymentEvery, graceOnPrincipalAndInterestPayment, graceOnArrearsAging);
+ }
+
+ public void updateLoanProduct(final LoanProduct loanProduct) {
+ this.loanProduct = loanProduct;
+ }
+
+ public static LoanProductConfigurableAttributes populateDefaultsForConfigurableAttributes() {
+ final Boolean amortization = true;
+ final Boolean interestMethod = true;
+ final Boolean transactionProcessingStrategy = true;
+ final Boolean interestCalcPeriod = true;
+ final Boolean arrearsTolerance = true;
+ final Boolean repaymentEvery = true;
+ final Boolean graceOnPrincipalAndInterestPayment = true;
+ final Boolean graceOnArrearsAging = true;
+
+ return new LoanProductConfigurableAttributes(amortization, interestMethod, transactionProcessingStrategy, interestCalcPeriod,
+ arrearsTolerance, repaymentEvery, graceOnPrincipalAndInterestPayment, graceOnArrearsAging);
+ }
+
+ public LoanProductConfigurableAttributes(Boolean amortization, Boolean interestMethod, Boolean transactionProcessingStrategy,
+ Boolean interestCalcPeriod, Boolean arrearsTolerance, Boolean repaymentEvery, Boolean graceOnPrincipalAndInterestPayment,
+ Boolean graceOnArrearsAging) {
+ this.amortizationType = amortization;
+ this.interestType = interestMethod;
+ this.inArrearsTolerance = arrearsTolerance;
+ this.graceOnArrearsAgeing = graceOnArrearsAging;
+ this.interestCalculationPeriodType = interestCalcPeriod;
+ this.graceOnPrincipalAndInterestPayment = graceOnPrincipalAndInterestPayment;
+ this.repaymentEvery = repaymentEvery;
+ this.transactionProcessingStrategyId = transactionProcessingStrategy;
+ }
+
+ protected LoanProductConfigurableAttributes() {
+
+ }
+
+ public static String[] getAllowedLoanConfigurableAttributes() {
+ return supportedloanConfigurableAttributes;
+ }
+
+ public LoanProduct getLoanProduct() {
+ return loanProduct;
+ }
+
+ public Boolean getAmortizationBoolean() {
+ return amortizationType;
+ }
+
+ public Boolean getInterestMethodBoolean() {
+ return interestType;
+ }
+
+ public Boolean getTransactionProcessingStrategyBoolean() {
+ return transactionProcessingStrategyId;
+ }
+
+ public Boolean getInterestCalcPeriodBoolean() {
+ return interestCalculationPeriodType;
+ }
+
+ public Boolean getArrearsToleranceBoolean() {
+ return inArrearsTolerance;
+ }
+
+ public Boolean getRepaymentEveryBoolean() {
+ return repaymentEvery;
+ }
+
+ public Boolean getGraceOnPrincipalAndInterestPaymentBoolean() {
+ return graceOnPrincipalAndInterestPayment;
+ }
+
+ public Boolean getGraceOnArrearsAgingBoolean() {
+ return graceOnArrearsAgeing;
+ }
+
+ public void setLoanProduct(LoanProduct loanProduct) {
+ this.loanProduct = loanProduct;
+ }
+
+ public void setAmortizationType(Boolean amortizationType) {
+ this.amortizationType = amortizationType;
+ }
+
+ public void setInterestType(Boolean interestType) {
+ this.interestType = interestType;
+ }
+
+ public void setTransactionProcessingStrategyId(Boolean transactionProcessingStrategyId) {
+ this.transactionProcessingStrategyId = transactionProcessingStrategyId;
+ }
+
+ public void setInterestCalculationPeriodType(Boolean interestCalculationPeriodType) {
+ this.interestCalculationPeriodType = interestCalculationPeriodType;
+ }
+
+ public void setInArrearsTolerance(Boolean inArrearsTolerance) {
+ this.inArrearsTolerance = inArrearsTolerance;
+ }
+
+ public void setRepaymentEvery(Boolean repaymentEvery) {
+ this.repaymentEvery = repaymentEvery;
+ }
+
+ public void setGraceOnPrincipalAndInterestPayment(Boolean graceOnPrincipalAndInterestPayment) {
+ this.graceOnPrincipalAndInterestPayment = graceOnPrincipalAndInterestPayment;
+ }
+
+ public void setGraceOnArrearsAgeing(Boolean graceOnArrearsAgeing) {
+ this.graceOnArrearsAgeing = graceOnArrearsAgeing;
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
index d3704437b..3bb708784 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
@@ -514,4 +514,57 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
public void updateNumberOfRepayments(Integer numberOfRepayments) {
this.numberOfRepayments = numberOfRepayments;
}
+
+ public Integer getGraceOnPrincipalPayment() {
+ return graceOnPrincipalPayment;
+ }
+
+ public void setGraceOnPrincipalPayment(Integer graceOnPrincipalPayment) {
+ this.graceOnPrincipalPayment = graceOnPrincipalPayment;
+ }
+
+ public Integer getGraceOnInterestPayment() {
+ return graceOnInterestPayment;
+ }
+
+ public void setGraceOnInterestPayment(Integer graceOnInterestPayment) {
+ this.graceOnInterestPayment = graceOnInterestPayment;
+ }
+
+ public Integer getGraceOnArrearsAgeing() {
+ return graceOnArrearsAgeing;
+ }
+
+ public void setGraceOnArrearsAgeing(Integer graceOnArrearsAgeing) {
+ this.graceOnArrearsAgeing = graceOnArrearsAgeing;
+ }
+
+ public void setInterestMethod(InterestMethod interestMethod) {
+ this.interestMethod = interestMethod;
+ }
+
+ public void setInterestCalculationPeriodMethod(InterestCalculationPeriodMethod interestCalculationPeriodMethod) {
+ this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
+ }
+
+ public void setRepayEvery(Integer repayEvery) {
+ this.repayEvery = repayEvery;
+ }
+
+ public void setRepaymentPeriodFrequencyType(PeriodFrequencyType repaymentPeriodFrequencyType) {
+ this.repaymentPeriodFrequencyType = repaymentPeriodFrequencyType;
+ }
+
+ public void setAmortizationMethod(AmortizationMethod amortizationMethod) {
+ this.amortizationMethod = amortizationMethod;
+ }
+
+ public void setInArrearsTolerance(BigDecimal inArrearsTolerance) {
+ this.inArrearsTolerance = inArrearsTolerance;
+ }
+
+ public BigDecimal getArrearsTolerance() {
+ return this.inArrearsTolerance;
+ }
+
}
\ No newline at end of file
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 eb43e2b6e..a15c914d6 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
@@ -31,6 +31,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionT
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@@ -76,7 +77,7 @@ public final class LoanProductDataValidator {
LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName,
LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstallmentParamName,
LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName, LoanProductConstants.canDefineEmiAmountParamName,
- LoanProductConstants.installmentAmountInMultiplesOfParamName));
+ LoanProductConstants.installmentAmountInMultiplesOfParamName, LoanProductConstants.allowAttributeOverridesParamName));
private final FromJsonHelper fromApiJsonHelper;
@@ -438,10 +439,41 @@ public final class LoanProductDataValidator {
}
validateMultiDisburseLoanData(baseDataValidator, element);
+
+ //validateLoanConfigurableAttributes(baseDataValidator,element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
+ private void validateLoanConfigurableAttributes(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
+
+ if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.allowAttributeOverridesParamName, element)) {
+
+ final JsonObject object = element.getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName);
+
+ Integer length = LoanProductConfigurableAttributes.supportedloanConfigurableAttributes.length;
+
+ Boolean bool[] = new Boolean[length];
+
+ for (int i = 0; i < length; i++) {
+ /* Validate the attribute names */
+ if (!this.fromApiJsonHelper.parameterExists(LoanProductConfigurableAttributes.supportedloanConfigurableAttributes[i],
+ object)) {
+ baseDataValidator.reset().parameter(LoanProductConstants.allowAttributeOverridesParamName)
+ .failWithCode("invalid.loan.configuration.attribute.passed");
+ } else {
+ bool[i] = this.fromApiJsonHelper.extractBooleanNamed(
+ LoanProductConfigurableAttributes.supportedloanConfigurableAttributes[i], object);
+ /* Validate the boolean value */
+ baseDataValidator.reset().parameter(LoanProductConstants.allowAttributeOverridesParamName).value(bool[i])
+ .ignoreIfNull().validateForBooleanValue();
+ }
+
+ }
+ }
+ }
+
+
private void validateMultiDisburseLoanData(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
Boolean multiDisburseLoan = false;
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.multiDisburseLoanParameterName, element)) {
@@ -888,6 +920,8 @@ public final class LoanProductDataValidator {
}
validateMultiDisburseLoanData(baseDataValidator, element);
+
+ //validateLoanConfigurableAttributes(baseDataValidator,element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
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 f0cd945ea..c4d153116 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
@@ -28,6 +28,7 @@ import org.mifosplatform.portfolio.loanproduct.data.LoanProductBorrowerCycleVari
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.LoanProductConfigurableAttributes;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductParamType;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
@@ -76,7 +77,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final String sql = "select " + rm.schema() + " where bc.loan_product_id=? order by bc.borrower_cycle_number,bc.value_condition";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanProductId });
}
-
+
@Override
public Collection retrieveAllLoanProducts() {
@@ -169,12 +170,16 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "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, "
+ "lp.account_moves_out_of_npa_only_on_arrears_completion as accountMovesOutOfNPAOnlyOnArrearsCompletion, "
- + "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId "
+ + "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId, "
+ + "lca.id as lcaId, lca.amortization_method_enum as amortizationBoolean, lca.interest_method_enum as interestMethodConfigBoolean, "
+ + "lca.loan_transaction_strategy_id as transactionProcessingStrategyBoolean,lca.interest_calculated_in_period_enum as interestCalcPeriodBoolean, lca.arrearstolerance_amount as arrearsToleranceBoolean, "
+ + "lca.repay_every as repaymentFrequencyBoolean, lca.moratorium as graceOnPrincipalAndInterestBoolean, lca.grace_on_arrears_ageing as graceOnArrearsAgingBoolean "
+ " 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"
+ + " left join m_product_loan_configurable_attributes lca on lca.loan_product_id = lp.id "
+ " join m_currency curr on curr.code = lp.currency_code";
}
@@ -301,6 +306,20 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
restFrequencyDate, isArrearsBasedOnOriginalSchedule);
}
+ final boolean amortization = rs.getBoolean("amortizationBoolean");
+ final boolean interestMethod = rs.getBoolean("interestMethodConfigBoolean");
+ final boolean transactionProcessingStrategy = rs.getBoolean("transactionProcessingStrategyBoolean");
+ final boolean interestCalcPeriod = rs.getBoolean("interestCalcPeriodBoolean");
+ final boolean arrearsTolerance = rs.getBoolean("arrearsToleranceBoolean");
+ final boolean repaymentFrequency = rs.getBoolean("repaymentFrequencyBoolean");
+ final boolean graceOnPrincipalAndInterest = rs.getBoolean("graceOnPrincipalAndInterestBoolean");
+ final boolean graceOnArrearsAging = rs.getBoolean("graceOnArrearsAgingBoolean");
+
+ LoanProductConfigurableAttributes allowAttributeOverrides = null;
+
+ allowAttributeOverrides = new LoanProductConfigurableAttributes(amortization, interestMethod, transactionProcessingStrategy,
+ interestCalcPeriod, arrearsTolerance, repaymentFrequency, graceOnPrincipalAndInterest, graceOnArrearsAging);
+
final boolean holdGuaranteeFunds = rs.getBoolean("holdGuaranteeFunds");
LoanProductGuaranteeData loanProductGuaranteeData = null;
if (holdGuaranteeFunds) {
@@ -326,7 +345,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData,
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
- installmentAmountInMultiplesOf);
+ installmentAmountInMultiplesOf, allowAttributeOverrides);
}
}
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 17c099ea4..530894a9c 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
@@ -110,7 +110,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
MifosEntityAccessType.OFFICE_ACCESS_TO_LOAN_PRODUCTS,
MifosEntityType.LOAN_PRODUCT,
loanproduct.getId());
-
+
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanproduct.getId()) //
diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V248__product_loan_configurable_attributes.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V248__product_loan_configurable_attributes.sql
new file mode 100644
index 000000000..7db8d65b8
--- /dev/null
+++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V248__product_loan_configurable_attributes.sql
@@ -0,0 +1,21 @@
+CREATE TABLE `m_product_loan_configurable_attributes` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `loan_product_id` BIGINT NOT NULL,
+ `amortization_method_enum` TINYINT NOT NULL DEFAULT '1',
+ `interest_method_enum` TINYINT NOT NULL DEFAULT '1',
+ `loan_transaction_strategy_id` TINYINT NOT NULL DEFAULT '1',
+ `interest_calculated_in_period_enum` TINYINT NOT NULL DEFAULT '1',
+ `arrearstolerance_amount` TINYINT NOT NULL DEFAULT '1',
+ `repay_every` TINYINT NOT NULL DEFAULT '1',
+ `moratorium` TINYINT NOT NULL DEFAULT '1',
+ `grace_on_arrears_ageing` TINYINT NOT NULL DEFAULT '1',
+ PRIMARY KEY (`id`),
+ KEY `fk_m_product_loan_configurable_attributes_0001` (`loan_product_id`),
+ CONSTRAINT `fk_m_product_loan_configurable_attributes_0001` FOREIGN KEY (`loan_product_id`) REFERENCES `m_product_loan` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+ INSERT into `m_product_loan_configurable_attributes`
+(loan_product_id,amortization_method_enum,interest_method_enum,loan_transaction_strategy_id,
+interest_calculated_in_period_enum,arrearstolerance_amount,repay_every,moratorium,grace_on_arrears_ageing)
+(select pl.id,'1','1','1','1','1','1','1','1' from `m_product_loan` pl );
\ No newline at end of file