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>
<td class=fielddesc>If Specified as true, arrears will be identified based on original schedule.<br>
</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>
</div>
</div>
@@ -12942,7 +12948,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
</td>
</tr>
</table>
@@ -13045,6 +13051,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",
@@ -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;
@@ -4239,6 +4240,7 @@ public class ClientLoanIntegrationTest {
}
}
/***
* Test case to verify Mifos Style payment strategy
*/
@@ -4772,5 +4774,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);
}
}
@@ -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);
}
@@ -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 {
@@ -97,7 +98,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<String, Object> map = new HashMap<>();
@@ -166,6 +170,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);
}
@@ -422,4 +431,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;
}
}
@@ -452,6 +452,7 @@ public class LoansApiResource {
}
Collection<LoanProductData> productOptions = null;
LoanProductData product = null;
Collection<EnumOptionData> loanTermFrequencyTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyTypeOptions = null;
Collection<TransactionProcessingStrategyData> 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();
@@ -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<LoanTermVariationsData> emiAmountVariations;
private final LoanProductData product;
private LoanProductData product;
private final Map<Long, LoanBorrowerCycleData> 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<DisbursementData> disbursementDetails,
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> 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;
}
@@ -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(),
@@ -89,5 +89,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";
}
@@ -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;
@@ -143,6 +144,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.
@@ -203,6 +205,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,
@@ -214,7 +217,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) {
@@ -274,6 +278,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,
@@ -285,7 +290,8 @@ public class LoanProductData {
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
installmentAmountInMultiplesOf);
installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
}
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
@@ -352,6 +358,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,
@@ -363,7 +370,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<String, Object> accountingMappings,
@@ -397,7 +405,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;
@@ -472,6 +480,7 @@ public class LoanProductData {
this.productGuaranteeData = loanProductGuaranteeData;
this.principalThresholdForLastInstallment = principalThresholdForLastInstallment;
this.accountMovesOutOfNPAOnlyOnArrearsCompletion = accountMovesOutOfNPAOnlyOnArrearsCompletion;
this.allowAttributeOverrides = allowAttributeOverrides;
this.daysInMonthTypeOptions = null;
this.daysInYearTypeOptions = null;
@@ -587,6 +596,7 @@ public class LoanProductData {
this.daysInYearTypeOptions = daysInYearTypeOptions;
this.interestRecalculationCompoundingTypeOptions = interestRecalculationCompoundingTypeOptions;
this.rescheduleStrategyTypeOptions = rescheduleStrategyTypeOptions;
this.allowAttributeOverrides = productData.allowAttributeOverrides;
if (CollectionUtils.isEmpty(interestRecalculationFrequencyTypeOptions)) {
this.interestRecalculationFrequencyTypeOptions = null;
@@ -850,4 +860,12 @@ public class LoanProductData {
public boolean 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.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;
@@ -136,6 +137,9 @@ public class LoanProduct extends AbstractPersistable<Long> {
@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;
@@ -237,6 +241,13 @@ public class LoanProduct extends AbstractPersistable<Long> {
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 +270,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment,
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 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 +543,11 @@ public class LoanProduct extends AbstractPersistable<Long> {
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 +572,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
this.transactionProcessingStrategy = strategy;
}
public LoanTransactionProcessingStrategy getRepaymentStrategy() {
return this.transactionProcessingStrategy;
}
public boolean hasCurrencyCodeOf(final String currencyCode) {
return this.loanProductRelatedDetail.hasCurrencyCodeOf(currencyCode);
}
@@ -583,6 +603,14 @@ public class LoanProduct extends AbstractPersistable<Long> {
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) {
final Map<String, Object> actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator);
@@ -751,6 +779,80 @@ public class LoanProduct extends AbstractPersistable<Long> {
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);
@@ -1077,4 +1179,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
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) {
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.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;
@@ -78,7 +79,7 @@ public final class LoanProductDataValidator {
LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstallmentParamName,
LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName, LoanProductConstants.canDefineEmiAmountParamName,
LoanProductConstants.installmentAmountInMultiplesOfParamName,
LoanProductConstants.preClosureInterestCalculationStrategyParamName));
LoanProductConstants.preClosureInterestCalculationStrategyParamName, LoanProductConstants.allowAttributeOverridesParamName));
private final FromJsonHelper fromApiJsonHelper;
@@ -441,9 +442,39 @@ 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)) {
@@ -901,6 +932,8 @@ public final class LoanProductDataValidator {
validateMultiDisburseLoanData(baseDataValidator, element);
// validateLoanConfigurableAttributes(baseDataValidator,element);
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.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<LoanProductData> retrieveAllLoanProducts() {
@@ -170,12 +171,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";
}
@@ -307,6 +312,20 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
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");
LoanProductGuaranteeData loanProductGuaranteeData = null;
if (holdGuaranteeFunds) {
@@ -332,7 +351,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData,
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
installmentAmountInMultiplesOf);
installmentAmountInMultiplesOf, allowAttributeOverrides);
}
}
@@ -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()) //
@@ -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 );