Merge branch 'loanConfig' of https://github.com/chandrikamohith/mifosx into chandrikamohith-loanConfig

Conflicts:
	mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java
	mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java
This commit is contained in:
Vishwas Babu A J
2015-04-06 17:11:19 -07:00
16 changed files with 701 additions and 21 deletions
+17 -1
View File
@@ -11676,6 +11676,12 @@ An enumeration that indicates the type of transaction processing strategy to be
<tr> <tr>
<td class=fielddesc>If Specified as true, arrears will be identified based on original schedule.<br> <td class=fielddesc>If Specified as true, arrears will be identified based on original schedule.<br>
</tr> </tr>
<tr class=alt>
<td>allowAttributeOverrides</td>
</tr>
<tr>
<td class=fielddesc>Specifies if select attributes may be overridden for individual loan accounts.<br>
</tr>
</table> </table>
</div> </div>
</div> </div>
@@ -12942,7 +12948,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
numberOfRepaymentVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle,
interestRateVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle,
multiDisburseLoan,maxTrancheCount, multiDisburseLoan,maxTrancheCount,
outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides
</td> </td>
</tr> </tr>
</table> </table>
@@ -13045,6 +13051,16 @@ Request Body:
"maxValue":"20" "maxValue":"20"
} }
], ],
"allowAttributeOverrides":
{amortizationType : true,
interestType : true,
transactionProcessingStrategyId : false,
interestCalculationPeriodType : true,
inArrearsTolerance : false,
repaymentEvery : true,
graceOnPrincipalAndInterestPayment : true,
graceOnArrearsAgeing : true},
"accountingRule":"2", "accountingRule":"2",
"name":"product 5", "name":"product 5",
"shortName":"prd5", "shortName":"prd5",
@@ -40,6 +40,7 @@ import org.mifosplatform.integrationtests.common.savings.SavingsAccountHelper;
import org.mifosplatform.integrationtests.common.savings.SavingsProductHelper; import org.mifosplatform.integrationtests.common.savings.SavingsProductHelper;
import org.mifosplatform.integrationtests.common.savings.SavingsStatusChecker; import org.mifosplatform.integrationtests.common.savings.SavingsStatusChecker;
import com.google.gson.JsonObject;
import com.jayway.restassured.builder.RequestSpecBuilder; import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder; import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType; import com.jayway.restassured.http.ContentType;
@@ -4239,6 +4240,7 @@ public class ClientLoanIntegrationTest {
} }
} }
/*** /***
* Test case to verify Mifos Style payment strategy * Test case to verify Mifos Style payment strategy
*/ */
@@ -4773,4 +4775,123 @@ public class ClientLoanIntegrationTest {
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec); 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);
}
} }
@@ -74,8 +74,9 @@ public class LoanApplicationTestBuilder {
map.put("expectedDisbursementDate", this.expectedDisbursmentDate); map.put("expectedDisbursementDate", this.expectedDisbursmentDate);
map.put("submittedOnDate", this.submittedOnDate); map.put("submittedOnDate", this.submittedOnDate);
map.put("loanType", this.loanType); map.put("loanType", this.loanType);
map.put("repaymentsStartingFromDate", this.repaymentsStartingFromDate); if(repaymentsStartingFromDate != null){
map.put("repaymentsStartingFromDate", this.repaymentsStartingFromDate);
}
if (charges != null) { if (charges != null) {
map.put("charges", charges); map.put("charges", charges);
} }
@@ -14,6 +14,7 @@ import org.mifosplatform.integrationtests.common.Utils;
import org.mifosplatform.integrationtests.common.accounting.Account; import org.mifosplatform.integrationtests.common.accounting.Account;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class LoanProductTestBuilder { public class LoanProductTestBuilder {
@@ -97,6 +98,9 @@ public class LoanProductTestBuilder {
private String minimumGuaranteeFromOwnFunds = null; private String minimumGuaranteeFromOwnFunds = null;
private String minimumGuaranteeFromGuarantor = null; private String minimumGuaranteeFromGuarantor = null;
private String isArrearsBasedOnOriginalSchedule = null; private String isArrearsBasedOnOriginalSchedule = null;
private String graceOnPrincipalPayment = "1";
private String graceOnInterestPayment = "1";
private JsonObject allowAttributeOverrides = null;
public String build(final String chargeId) { public String build(final String chargeId) {
final HashMap<String, Object> map = new HashMap<>(); final HashMap<String, Object> map = new HashMap<>();
@@ -166,6 +170,11 @@ public class LoanProductTestBuilder {
map.put("minimumGuaranteeFromOwnFunds", this.minimumGuaranteeFromOwnFunds); 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); return new Gson().toJson(map);
} }
@@ -422,4 +431,14 @@ public class LoanProductTestBuilder {
return this; 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;
}
} }
@@ -452,6 +452,7 @@ public class LoansApiResource {
} }
Collection<LoanProductData> productOptions = null; Collection<LoanProductData> productOptions = null;
LoanProductData product = null;
Collection<EnumOptionData> loanTermFrequencyTypeOptions = null; Collection<EnumOptionData> loanTermFrequencyTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyTypeOptions = null; Collection<EnumOptionData> repaymentFrequencyTypeOptions = null;
Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = null; Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = null;
@@ -472,6 +473,8 @@ public class LoansApiResource {
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if (template) { if (template) {
productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup(); productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
product = this.loanProductReadPlatformService.retrieveLoanProduct(loanBasicDetails.loanProductId());
loanBasicDetails.setProduct(product);
loanTermFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions(); loanTermFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
repaymentFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions(); repaymentFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
interestRateFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions(); interestRateFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
@@ -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.LoanProductBorrowerCycleVariationData;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData; 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.loanproduct.domain.LoanProductValueConditionType;
import org.mifosplatform.portfolio.note.data.NoteData; import org.mifosplatform.portfolio.note.data.NoteData;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -162,7 +163,7 @@ public class LoanAccountData {
private final Collection<LoanTermVariationsData> emiAmountVariations; private final Collection<LoanTermVariationsData> emiAmountVariations;
private final LoanProductData product; private LoanProductData product;
private final Map<Long, LoanBorrowerCycleData> memberVariations; private final Map<Long, LoanBorrowerCycleData> memberVariations;
@@ -300,6 +301,7 @@ public class LoanAccountData {
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance); createStandingInstructionAtDisbursement, paidInAdvance);
} }
/** /**
@@ -420,6 +422,7 @@ public class LoanAccountData {
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance); createStandingInstructionAtDisbursement, paidInAdvance);
} }
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) { 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, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement,
paidInAdvance); paidInAdvance);
} }
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) { 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.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance); acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
} }
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product, 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, acc.memberVariations, product, acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA,
product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement, paidInAdvance); product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement, paidInAdvance);
} }
/* /*
@@ -883,6 +889,10 @@ public class LoanAccountData {
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails, final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges, final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges,
final PaidInAdvanceData paidInAdvance) { 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, 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, 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.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance); acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
} }
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) { 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 EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule, final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance) { final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance) {
this.id = id; this.id = id;
this.accountNo = accountNo; this.accountNo = accountNo;
this.status = status; this.status = status;
@@ -1273,6 +1285,9 @@ public class LoanAccountData {
this.originalSchedule = originalSchedule; this.originalSchedule = originalSchedule;
this.createStandingInstructionAtDisbursement = createStandingInstructionAtDisbursement; this.createStandingInstructionAtDisbursement = createStandingInstructionAtDisbursement;
this.paidInAdvance = paidInAdvance; this.paidInAdvance = paidInAdvance;
if(this.product != null){
this.product.setloanProductConfigurableAttributes(product.getloanProductConfigurableAttributes());
}
} }
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() { public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
@@ -1342,6 +1357,10 @@ public class LoanAccountData {
return this.product; return this.product;
} }
public void setProduct(LoanProductData product){
this.product = product;
}
public GroupGeneralData groupData() { public GroupGeneralData groupData() {
return this.group; return this.group;
} }
@@ -232,6 +232,11 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
validateSubmittedOnDate(newLoanApplication); validateSubmittedOnDate(newLoanApplication);
final LoanProductRelatedDetail productRelatedDetail = newLoanApplication.repaymentScheduleDetail(); final LoanProductRelatedDetail productRelatedDetail = newLoanApplication.repaymentScheduleDetail();
if(loanProduct.getLoanProductConfigurableAttributes() != null){
updateProductRelatedDetails(productRelatedDetail,newLoanApplication);
}
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(newLoanApplication.getTermFrequency(), this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(newLoanApplication.getTermFrequency(),
newLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(), newLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(), 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) { private void createAndPersistCalendarInstanceForInterestRecalculation(final Loan loan) {
LocalDate calendarStartDate = loan.loanInterestRecalculationDetails().getRestFrequencyLocalDate(); LocalDate calendarStartDate = loan.loanInterestRecalculationDetails().getRestFrequencyLocalDate();
@@ -462,9 +504,9 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final String productIdParamName = "productId"; final String productIdParamName = "productId";
if (changes.containsKey(productIdParamName)) { if (changes.containsKey(productIdParamName)) {
final Long productId = command.longValueOfParameterNamed(productIdParamName); final Long productId = command.longValueOfParameterNamed(productIdParamName);
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId); final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); } if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
existingLoanApplication.updateLoanProduct(loanProduct); existingLoanApplication.updateLoanProduct(loanProduct);
if (!changes.containsKey("interestRateFrequencyType")) { if (!changes.containsKey("interestRateFrequencyType")) {
@@ -494,6 +536,11 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateIsInterestRecalculationEnabled(); existingLoanApplication.updateIsInterestRecalculationEnabled();
validateSubmittedOnDate(existingLoanApplication); validateSubmittedOnDate(existingLoanApplication);
final LoanProductRelatedDetail productRelatedDetail = existingLoanApplication.repaymentScheduleDetail();
if(existingLoanApplication.loanProduct().getLoanProductConfigurableAttributes() != null){
updateProductRelatedDetails(productRelatedDetail,existingLoanApplication);
}
final String fundIdParamName = "fundId"; final String fundIdParamName = "fundId";
if (changes.containsKey(fundIdParamName)) { if (changes.containsKey(fundIdParamName)) {
final Long fundId = command.longValueOfParameterNamed(fundIdParamName); final Long fundId = command.longValueOfParameterNamed(fundIdParamName);
@@ -546,7 +593,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateLoanCharges(possiblyModifedLoanCharges); existingLoanApplication.updateLoanCharges(possiblyModifedLoanCharges);
} }
final LoanProductRelatedDetail productRelatedDetail = existingLoanApplication.repaymentScheduleDetail();
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(), this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(),
existingLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(), existingLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(), productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
@@ -90,4 +90,14 @@ public interface LoanProductConstants {
public static final String canDefineEmiAmountParamName = "canDefineInstallmentAmount"; public static final String canDefineEmiAmountParamName = "canDefineInstallmentAmount";
public static final String installmentAmountInMultiplesOfParamName = "installmentAmountInMultiplesOf"; 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";
} }
@@ -30,6 +30,7 @@ import org.mifosplatform.portfolio.loanaccount.data.LoanInterestRecalculationDat
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod; import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod; 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.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.paymenttype.data.PaymentTypeData; import org.mifosplatform.portfolio.paymenttype.data.PaymentTypeData;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -143,6 +144,7 @@ public class LoanProductData {
private final Boolean holdGuaranteeFunds; private final Boolean holdGuaranteeFunds;
private final LoanProductGuaranteeData productGuaranteeData; private final LoanProductGuaranteeData productGuaranteeData;
private final Boolean accountMovesOutOfNPAOnlyOnArrearsCompletion; private final Boolean accountMovesOutOfNPAOnlyOnArrearsCompletion;
private LoanProductConfigurableAttributes allowAttributeOverrides;
/** /**
* Used when returning lookup information about loan product for dropdowns. * Used when returning lookup information about loan product for dropdowns.
@@ -203,6 +205,7 @@ public class LoanProductData {
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null; final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
final boolean canDefineInstallmentAmount = false; final boolean canDefineInstallmentAmount = false;
final Integer installmentAmountInMultiplesOf = null; final Integer installmentAmountInMultiplesOf = null;
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -214,7 +217,8 @@ public class LoanProductData {
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
installmentAmountInMultiplesOf); installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
} }
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) { public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
@@ -274,6 +278,7 @@ public class LoanProductData {
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false; final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false;
final boolean canDefineInstallmentAmount = false; final boolean canDefineInstallmentAmount = false;
final Integer installmentAmountInMultiplesOf = null; final Integer installmentAmountInMultiplesOf = null;
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -285,7 +290,8 @@ public class LoanProductData {
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
installmentAmountInMultiplesOf); installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
} }
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() { public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
@@ -352,6 +358,7 @@ public class LoanProductData {
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false; final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false;
final boolean canDefineInstallmentAmount = false; final boolean canDefineInstallmentAmount = false;
final Integer installmentAmountInMultiplesOf = null; final Integer installmentAmountInMultiplesOf = null;
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -363,7 +370,8 @@ public class LoanProductData {
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType,
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment,
holdGuaranteeFunds, productGuaranteeData, principalThresholdForLastInstallment, holdGuaranteeFunds, productGuaranteeData, principalThresholdForLastInstallment,
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf); accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
} }
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings, public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
@@ -397,7 +405,7 @@ public class LoanProductData {
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds, final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds,
final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstallment, final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstallment,
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean canDefineInstallmentAmount, final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean canDefineInstallmentAmount,
Integer installmentAmountInMultiplesOf) { Integer installmentAmountInMultiplesOf, LoanProductConfigurableAttributes allowAttributeOverrides) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.shortName = shortName; this.shortName = shortName;
@@ -472,6 +480,7 @@ public class LoanProductData {
this.productGuaranteeData = loanProductGuaranteeData; this.productGuaranteeData = loanProductGuaranteeData;
this.principalThresholdForLastInstallment = principalThresholdForLastInstallment; this.principalThresholdForLastInstallment = principalThresholdForLastInstallment;
this.accountMovesOutOfNPAOnlyOnArrearsCompletion = accountMovesOutOfNPAOnlyOnArrearsCompletion; this.accountMovesOutOfNPAOnlyOnArrearsCompletion = accountMovesOutOfNPAOnlyOnArrearsCompletion;
this.allowAttributeOverrides = allowAttributeOverrides;
this.daysInMonthTypeOptions = null; this.daysInMonthTypeOptions = null;
this.daysInYearTypeOptions = null; this.daysInYearTypeOptions = null;
@@ -587,6 +596,7 @@ public class LoanProductData {
this.daysInYearTypeOptions = daysInYearTypeOptions; this.daysInYearTypeOptions = daysInYearTypeOptions;
this.interestRecalculationCompoundingTypeOptions = interestRecalculationCompoundingTypeOptions; this.interestRecalculationCompoundingTypeOptions = interestRecalculationCompoundingTypeOptions;
this.rescheduleStrategyTypeOptions = rescheduleStrategyTypeOptions; this.rescheduleStrategyTypeOptions = rescheduleStrategyTypeOptions;
this.allowAttributeOverrides = productData.allowAttributeOverrides;
if (CollectionUtils.isEmpty(interestRecalculationFrequencyTypeOptions)) { if (CollectionUtils.isEmpty(interestRecalculationFrequencyTypeOptions)) {
this.interestRecalculationFrequencyTypeOptions = null; this.interestRecalculationFrequencyTypeOptions = null;
@@ -850,4 +860,12 @@ public class LoanProductData {
public boolean canDefineInstallmentAmount() { public boolean canDefineInstallmentAmount() {
return this.canDefineInstallmentAmount; return this.canDefineInstallmentAmount;
} }
public LoanProductConfigurableAttributes getloanProductConfigurableAttributes() {
return this.allowAttributeOverrides;
}
public void setloanProductConfigurableAttributes(LoanProductConfigurableAttributes loanProductConfigurableAttributes) {
this.allowAttributeOverrides = loanProductConfigurableAttributes;
}
} }
@@ -46,6 +46,7 @@ import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants; import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@@ -136,6 +137,9 @@ public class LoanProduct extends AbstractPersistable<Long> {
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true) @OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
private LoanProductGuaranteeDetails loanProductGuaranteeDetails; 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) @Column(name = "principal_threshold_for_last_installment", scale = 2, precision = 5, nullable = false)
private BigDecimal principalThresholdForLastInstallment; private BigDecimal principalThresholdForLastInstallment;
@@ -237,6 +241,13 @@ public class LoanProduct extends AbstractPersistable<Long> {
loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command); loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command);
} }
LoanProductConfigurableAttributes loanConfigurableAttributes = null;
if (command.parameterExists(LoanProductConstants.allowAttributeOverridesParamName)) {
loanConfigurableAttributes = LoanProductConfigurableAttributes.createFrom(command);
} else {
loanConfigurableAttributes = LoanProductConfigurableAttributes.populateDefaultsForConfigurableAttributes();
}
BigDecimal principalThresholdForLastInstallment = command BigDecimal principalThresholdForLastInstallment = command
.bigDecimalValueOfParameterNamed(LoanProductConstants.principalThresholdForLastInstallmentParamName); .bigDecimalValueOfParameterNamed(LoanProductConstants.principalThresholdForLastInstallmentParamName);
@@ -259,7 +270,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment, isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment,
holdGuarantorFunds, loanProductGuaranteeDetails, principalThresholdForLastInstallment, holdGuarantorFunds, loanProductGuaranteeDetails, principalThresholdForLastInstallment,
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineEmiAmount, installmentAmountInMultiplesOf); accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineEmiAmount, installmentAmountInMultiplesOf, loanConfigurableAttributes);
} }
@@ -486,7 +497,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds, final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds,
final LoanProductGuaranteeDetails loanProductGuaranteeDetails, final BigDecimal principalThresholdForLastInstallment, final LoanProductGuaranteeDetails loanProductGuaranteeDetails, final BigDecimal principalThresholdForLastInstallment,
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, final boolean canDefineEmiAmount, final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, final boolean canDefineEmiAmount,
final Integer installmentAmountInMultiplesOf) { final Integer installmentAmountInMultiplesOf, final LoanProductConfigurableAttributes loanProductConfigurableAttributes) {
this.fund = fund; this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy; this.transactionProcessingStrategy = transactionProcessingStrategy;
this.name = name.trim(); this.name = name.trim();
@@ -532,6 +543,11 @@ public class LoanProduct extends AbstractPersistable<Long> {
for (LoanProductBorrowerCycleVariations borrowerCycleVariations : this.borrowerCycleVariations) { for (LoanProductBorrowerCycleVariations borrowerCycleVariations : this.borrowerCycleVariations) {
borrowerCycleVariations.updateLoanProduct(this); borrowerCycleVariations.updateLoanProduct(this);
} }
if (loanProductConfigurableAttributes != null) {
this.loanConfigurableAttributes = loanProductConfigurableAttributes;
loanConfigurableAttributes.updateLoanProduct(this);
}
this.loanProducTrancheDetails = new LoanProductTrancheDetails(multiDisburseLoan, maxTrancheCount, outstandingLoanBalance); this.loanProducTrancheDetails = new LoanProductTrancheDetails(multiDisburseLoan, maxTrancheCount, outstandingLoanBalance);
this.overdueDaysForNPA = overdueDaysForNPA; this.overdueDaysForNPA = overdueDaysForNPA;
this.productInterestRecalculationDetails = productInterestRecalculationDetails; this.productInterestRecalculationDetails = productInterestRecalculationDetails;
@@ -556,6 +572,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
this.transactionProcessingStrategy = strategy; this.transactionProcessingStrategy = strategy;
} }
public LoanTransactionProcessingStrategy getRepaymentStrategy() {
return this.transactionProcessingStrategy;
}
public boolean hasCurrencyCodeOf(final String currencyCode) { public boolean hasCurrencyCodeOf(final String currencyCode) {
return this.loanProductRelatedDetail.hasCurrencyCodeOf(currencyCode); return this.loanProductRelatedDetail.hasCurrencyCodeOf(currencyCode);
} }
@@ -583,6 +603,14 @@ public class LoanProduct extends AbstractPersistable<Long> {
return this.accountingRule; return this.accountingRule;
} }
public void update(final LoanProductConfigurableAttributes loanConfigurableAttributes) {
this.loanConfigurableAttributes = loanConfigurableAttributes;
}
public LoanProductConfigurableAttributes getLoanProductConfigurableAttributes() {
return this.loanConfigurableAttributes;
}
public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) { public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) {
final Map<String, Object> actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator); final Map<String, Object> actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator);
@@ -751,6 +779,80 @@ public class LoanProduct extends AbstractPersistable<Long> {
this.holdGuaranteeFunds = newValue; 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 (actualChanges.containsKey(LoanProductConstants.holdGuaranteeFundsParamName)) {
if (this.holdGuaranteeFunds) { if (this.holdGuaranteeFunds) {
this.loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command); this.loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command);
@@ -1077,4 +1179,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
return preCloseInterestCalculationStrategy; return preCloseInterestCalculationStrategy;
} }
public LoanProductRelatedDetail getLoanProductRelatedDetail() {
return loanProductRelatedDetail;
}
} }
@@ -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<Long> {
@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;
}
}
@@ -514,4 +514,57 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
public void updateNumberOfRepayments(Integer numberOfRepayments) { public void updateNumberOfRepayments(Integer numberOfRepayments) {
this.numberOfRepayments = 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;
}
} }
@@ -32,6 +32,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionT
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType; import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@@ -78,7 +79,7 @@ public final class LoanProductDataValidator {
LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstallmentParamName, LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstallmentParamName,
LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName, LoanProductConstants.canDefineEmiAmountParamName, LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName, LoanProductConstants.canDefineEmiAmountParamName,
LoanProductConstants.installmentAmountInMultiplesOfParamName, LoanProductConstants.installmentAmountInMultiplesOfParamName,
LoanProductConstants.preClosureInterestCalculationStrategyParamName)); LoanProductConstants.preClosureInterestCalculationStrategyParamName, LoanProductConstants.allowAttributeOverridesParamName));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -441,9 +442,39 @@ public final class LoanProductDataValidator {
validateMultiDisburseLoanData(baseDataValidator, element); validateMultiDisburseLoanData(baseDataValidator, element);
// validateLoanConfigurableAttributes(baseDataValidator,element);
throwExceptionIfValidationWarningsExist(dataValidationErrors); 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) { private void validateMultiDisburseLoanData(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
Boolean multiDisburseLoan = false; Boolean multiDisburseLoan = false;
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.multiDisburseLoanParameterName, element)) { if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.multiDisburseLoanParameterName, element)) {
@@ -901,6 +932,8 @@ public final class LoanProductDataValidator {
validateMultiDisburseLoanData(baseDataValidator, element); validateMultiDisburseLoanData(baseDataValidator, element);
// validateLoanConfigurableAttributes(baseDataValidator,element);
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -28,6 +28,7 @@ import org.mifosplatform.portfolio.loanproduct.data.LoanProductBorrowerCycleVari
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductGuaranteeData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductGuaranteeData;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductInterestRecalculationData; 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.domain.LoanProductParamType;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException; import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -170,12 +171,16 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lpg.id as lpgId, lpg.mandatory_guarantee as mandatoryGuarantee, " + "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, " + "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, " + "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 " + " from m_product_loan lp "
+ " left join m_fund f on f.id = lp.fund_id " + " 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_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 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 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"; + " join m_currency curr on curr.code = lp.currency_code";
} }
@@ -307,6 +312,20 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
restFrequencyDate, isArrearsBasedOnOriginalSchedule, preCloseInterestCalculationStrategy); restFrequencyDate, isArrearsBasedOnOriginalSchedule, preCloseInterestCalculationStrategy);
} }
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"); final boolean holdGuaranteeFunds = rs.getBoolean("holdGuaranteeFunds");
LoanProductGuaranteeData loanProductGuaranteeData = null; LoanProductGuaranteeData loanProductGuaranteeData = null;
if (holdGuaranteeFunds) { if (holdGuaranteeFunds) {
@@ -332,7 +351,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData,
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
installmentAmountInMultiplesOf); installmentAmountInMultiplesOf, allowAttributeOverrides);
} }
} }
@@ -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 );