You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by an...@apache.org on 2019/06/12 22:33:03 UTC

[fineract] branch Fineract-614 updated (ba42a10 -> 5ac6ae7)

This is an automated email from the ASF dual-hosted git repository.

angelboxes pushed a change to branch Fineract-614
in repository https://gitbox.apache.org/repos/asf/fineract.git.


    omit ba42a10  Fineract-614
    omit aaa6169  A new rates module was added to define new rates that can be used to set min and max nominal interest rate, when a new loan account is created they can be used to determine which rates may be applicable for the loan account.
     add b03e70f  FINERACT-718
     add 210e380  Merge pull request #551 from vishwasbabu/develop
     add b3320b7  fixed broken link to section and closed tags appropriately
     add 80ae332  Merge pull request #552 from muarachmann/FINERACT-738
     add 3ee051c  Loan Disbursement validation - timezone of tenant
     add 2ae9166  LocalDate check should be according to timezone of tenant.
     add 746090c   Placed break tag appropriately and removed blank spaces in create client section, corrected grammatical errors and added punctuation to sentences.
     add 8af4943  Merge pull request #553 from kangbreder/fix-tag
     add f8c5de3  [FINERACT-747] change String.replaceAll() to replace() to improve performance
     add 22716d7  Merge pull request #558 from bd2019us/FINERACT-747-PATCH
     add b8d4afe  Fixed typos in SPM API Java Files
     add 67ba10f  Merge pull request #561 from apoorva-mk/FINERACT-748
     add bbb32f5  FINERACT-751 Fix CenterIntegrationTest Case
     add 57b7303  Merge pull request #564 from awasum/develop
     add 7bf648a  Revert "Adding a compress response filter,registering filter"
     add 82568d9  FINERACT-757: Use of sqlEncodeString function isn't needed as parameters are passed as an array of Objects and SqlInjection validation is done by Spring JdbcTemplate.
     add cc05940  Merge pull request #572 from angelboxes/FINERACT-757
     add 61045ca  FINERACT-758
     add 93e0137  Merge pull request #573 from vishwasbabu/develop
     new 4e3d36c  A new rates module was added to define new rates that can be used to set min and max nominal interest rate, when a new loan account is created they can be used to determine which rates may be applicable for the loan account.
     new 5ac6ae7  FINERACT-737: Updated Api Docs to include loan charges paid by FINERACT-736: Api Docs were updated to include rates documentation FINERACT-735: Integration test were created for rates creation, retrieval and update

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (ba42a10)
            \
             N -- N -- N   refs/heads/Fineract-614 (5ac6ae7)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 api-docs/apiLive.htm                               | 375 ++++++++++++++++-----
 fineract-provider/dev-dependencies.gradle          |   2 +-
 .../integrationtests/CenterIntegrationTest.java    |  41 +--
 .../{XBRLIntegrationTest.java => RatesTest.java}   |  53 +--
 .../integrationtests/common/rates/RatesHelper.java |  91 +++++
 .../shares/ShareAccountIntegrationTests.java       |   3 +-
 .../rule/api/AccountingRuleJsonInputParams.java    |   4 +-
 .../FixedDepositTransactionWorkbookPopulator.java  |   2 +-
 .../LoanRepaymentWorkbookPopulator.java            |   4 +-
 .../SavingsTransactionsWorkbookPopulator.java      |   2 +-
 .../core/boot/WebXmlConfiguration.java             |   3 -
 .../core/filters/ResponseCompressFilter.java       |  43 ---
 .../core/writer/GZipResponseWriter.java            |  45 ---
 .../service/ClientReadPlatformServiceImpl.java     |   8 +-
 .../portfolio/common/domain/ConditionType.java     |   4 +-
 .../service/CenterReadPlatformServiceImpl.java     |   6 +-
 .../service/GroupReadPlatformServiceImpl.java      |   6 +-
 .../portfolio/loanaccount/domain/Loan.java         |   6 +-
 .../service/LoanReadPlatformServiceImpl.java       |   2 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |   4 +-
 .../fineract/portfolio/rate/data/RateData.java     |   8 +-
 .../fineract/portfolio/rate/domain/Rate.java       |  36 +-
 .../domain/RateAppliesTo.java}                     |  48 +--
 .../service/RateEnumerations.java}                 |  30 +-
 .../rate/service/RateReadServiceImpl.java          |  12 +-
 .../fineract/spm/api/LookupTableApiResource.java   |   2 +-
 .../fineract/spm/api/ScorecardApiResource.java     |   2 +-
 .../sql/migrations/core_db/V352__rates.sql         |   3 +-
 28 files changed, 543 insertions(+), 302 deletions(-)
 copy fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/{XBRLIntegrationTest.java => RatesTest.java} (62%)
 create mode 100644 fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/rates/RatesHelper.java
 delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/filters/ResponseCompressFilter.java
 delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/writer/GZipResponseWriter.java
 copy fineract-provider/src/main/java/org/apache/fineract/portfolio/{charge/domain/ChargePaymentMode.java => rate/domain/RateAppliesTo.java} (55%)
 mode change 100755 => 100644
 copy fineract-provider/src/main/java/org/apache/fineract/portfolio/{group/service/GroupTypeEnumerations.java => rate/service/RateEnumerations.java} (58%)


[fineract] 02/02: FINERACT-737: Updated Api Docs to include loan charges paid by FINERACT-736: Api Docs were updated to include rates documentation FINERACT-735: Integration test were created for rates creation, retrieval and update

Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

angelboxes pushed a commit to branch Fineract-614
in repository https://gitbox.apache.org/repos/asf/fineract.git

commit 5ac6ae79c81584a79b0af02d4c40ab0147c48201
Author: Angel Cajas <an...@bowpi.com>
AuthorDate: Mon May 27 09:35:15 2019 -0600

    FINERACT-737: Updated Api Docs to include loan charges paid by
    FINERACT-736: Api Docs were updated to include rates documentation
    FINERACT-735: Integration test were created for rates creation, retrieval and update
---
 api-docs/apiLive.htm                               | 227 ++++++++++++++++++++-
 .../fineract/integrationtests/RatesTest.java       |  71 +++++++
 .../integrationtests/common/rates/RatesHelper.java |  91 +++++++++
 .../fineract/portfolio/rate/data/RateData.java     |   8 +-
 .../fineract/portfolio/rate/domain/Rate.java       |  36 ++--
 .../portfolio/rate/domain/RateAppliesTo.java       |  66 ++++++
 .../portfolio/rate/service/RateEnumerations.java   |  47 +++++
 .../rate/service/RateReadServiceImpl.java          |  12 +-
 .../sql/migrations/core_db/V352__rates.sql         |   3 +-
 9 files changed, 533 insertions(+), 28 deletions(-)

diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 67dfc71..b91380c 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -2546,6 +2546,24 @@
 								<td></td>
 								<td></td>
 							</tr>
+                            <tr>
+                                <td><a href="#rates">Rates</a></td>
+                                <td>rates</td>
+                                <td><a href="#create_rate">Create Rate</a></td>
+                                <td><a href="#rate_list">List Rates</a></td>
+                                <td></td>
+                                <td></td>
+                                <td></td>
+                            </tr>
+                            <tr>
+                                <td></td>
+                                <td>rates/{rateId}</td>
+                                <td></td>
+                                <td><a href="#retrieve_rate">Retrieve Rate</a></td>
+                                <td><a href="#update_rate">Update Rate</a></td>
+                                <td></td>
+                                <td></td>
+                            </tr>
 						</table>
 					</div>
 				</div>
@@ -12141,7 +12159,17 @@ GET https://DomainName/api/v1/loans/{loanId}/transactions/{transactionId}
     "displayLabel": "US Dollar ($)"
   },
   "amount": 559.88,
-  "interestPortion": 559.88
+  "interestPortion": 559.88,
+  "loanChargePaidByList": [
+    {
+      "id": 29,
+      "amount": 138.700000,
+      "installmentNumber": 0,
+      "chargeId": 7,
+      "transactionId": 35,
+      "name": "Late Penalty Charge"
+    } 
+  ]
 }
 					</code>
 				</div>
@@ -16173,7 +16201,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
 							numberOfRepaymentVariationsForBorrowerCycle,
 							interestRateVariationsForBorrowerCycle,
 							multiDisburseLoan,maxTrancheCount,
-							outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides, allowPartialPeriodInterestCalcualtion
+							outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides, allowPartialPeriodInterestCalcualtion, rates
 							</td>
 						</tr>
 					</table>
@@ -43264,7 +43292,198 @@ No Request Body:
 			</div>
 			<!-- End of Tax Group -->
 
-		<!-- start of clients api docs -->
+            <!-- Start of Rates-->
+            <a id="rates" name="rates" class="old-syle-anchor">&nbsp;</a>
+            <div class="method-section">
+                <div class="method-description">
+                    <h3>Rates</h3>
+                    <p>This defines the Rates</p>
+                    <table class=matrixHeading>
+                        <tr class="matrixHeadingBG">
+                            <td>
+                                <div class="fineractHeading2">Field Descriptions</div>
+                            </td>
+                        </tr>
+                        <tr class=alt>
+                            <td>name</td>
+                        </tr>
+                        <tr>
+                            <td class=fielddesc>Name of Rate
+                            </td>
+                        </tr>
+                        <tr class=alt>
+                            <td>productApply</td>
+                        </tr>
+                        <tr>
+                            <td class=fielddesc>
+                                Enum value that defines to which entity the rate will be applicable.
+                                Available options are:
+                                1=Loans
+
+                                Once a rate definition is created, this attribute cannot be changed at any point
+                            </td>
+                        </tr>
+                        <tr class=alt>
+                            <td>Active</td>
+                        </tr>
+                        <tr>
+                            <td class=fielddesc>
+                                Flag indicating if the rate is currently active
+                            </td>
+                        </tr>
+                    </table>
+                </div>
+            </div>
+            <a id="create_rate" name="create_rate"
+               class="old-syle-anchor">&nbsp;</a>
+            <div class="method-section">
+                <div class="method-description">
+                    <h4>Create a new Rate</h4>
+                    <p>Creates a new Rate</p>
+                    <table class=matrixHeading>
+                        <tr class="matrixHeadingBG">
+                            <td>
+                                <div class="fineractHeading2">Mandatory Fields</div>
+                            </td>
+                        </tr>
+                        <tr class=alt>
+                            <td>name</td>
+                        </tr>
+                        <tr class=alt>
+                            <td>percentage</td>
+                        </tr>
+                        <tr class=alt>
+                            <td>productApply</td>
+                        </tr>
+                        <tr class=alt>
+                            <td>active</td>
+                        </tr>
+                    </table>
+                </div>
+                <div class="method-example">
+                    <code class="method-declaration">
+                        POST https://DomainName/api/v1/rates
+                    </code>
+                    <code class="method-request">
+                        POST rates
+                        Content-Type: application/json
+                        Request Body:
+                        {
+                        "productApply":1,
+                        "name":"Loan rate",
+                        "percentage":"12",
+                        "active":true,
+                        "locale":"en"
+                        }
+                    </code>
+                    <code class="method-response">
+                        {
+                        "resourceId": 1
+                        }
+                    </code>
+                </div>
+            </div>
+            <a id="rate_list" name="rate_list"
+               class="old-syle-anchor">&nbsp;</a>
+            <div class="method-section">
+                <div class="method-description">
+                    <h4>List Rates</h4>
+                    <p>List Rates</p>
+                </div>
+                <div class="method-example">
+                    <code class="method-declaration">
+                        GET https://DomainName/api/v1/rates
+                    </code>
+                    <code class="method-request">
+                        GET rates
+                        Content-Type: application/json
+                    </code>
+                    <code class="method-response">
+                        [
+                        {
+                        "id":1,
+                        "name":"Loan Rate",
+                        "percentage":12.00,
+                        "productApply":1,
+                        "active":true
+                        },
+                        {
+                        "id":2,
+                        "name":"Public Rate",
+                        "percentage":30.00,
+                        "productApply":1,
+                        "active":true
+                        },
+                        {
+                        "id":3,
+                        "name":"Private Rate",
+                        "percentage":14.00,
+                        "productApply":1,
+                        "active":true
+                        }
+                        ]
+                    </code>
+                </div>
+            </div>
+            <a id="retrieve_rate" name="retrieve_rate"
+               class="old-syle-anchor">&nbsp;</a>
+            <div class="method-section">
+                <div class="method-description">
+                    <h4>Retrieve a Rate</h4>
+                    <p>Return the details of an existing Rate</p>
+                </div>
+                <div class="method-example">
+                    <code class="method-declaration">
+                        GET https://DomainName/api/v1/rates/{rateId}
+                    </code>
+                    <code class="method-request">
+                        GET rates/1
+                        Content-Type: application/json
+                    </code>
+                    <code class="method-response">
+                        {
+                        "id":1,
+                        "name":"Loan Rate",
+                        "percentage":12.00,
+                        "productApply":1,
+                        "active":true
+                        }
+                    </code>
+                </div>
+            </div>
+            <a id="update_rate" name="update_rate"
+               class="old-syle-anchor">&nbsp;</a>
+            <div class="method-section">
+                <div class="method-description">
+                    <h4>Update a Rate</h4>
+                    <p>Updates an existing Rate</p>
+                </div>
+                <div class="method-example">
+                    <code class="method-declaration">
+                        POST https://DomainName/api/v1/rates/{rateId}
+                    </code>
+                    <code class="method-request">
+                        POST rates/3
+                        Content-Type: application/json
+                        Request Body:
+                        {
+                        "percentage":"17",
+                        }
+                    </code>
+                    <code class="method-response">
+                        {
+                        "resourceId":3,
+                        "changes":
+                        {
+                        "percentage":17
+                        }
+                        }
+                    </code>
+                </div>
+            </div>
+            <!-- End of Rates -->
+
+            <!-- start of clients api docs -->
 		<a id="templates" name="clients" class="old-syle-anchor">&nbsp;</a>
 		<div class="method-section">
 			<div class="method-description">
@@ -46736,7 +46955,7 @@ GET https://DomainName/api/v1/self/loans/{loanId}/transactions/{transactionId}
   },
   "amount": 559.88,
   "interestPortion": 559.88
-}
+   }
 					</code>
 				</div>
 			</div>
diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/RatesTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/RatesTest.java
new file mode 100644
index 0000000..0feb648
--- /dev/null
+++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/RatesTest.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import com.jayway.restassured.builder.RequestSpecBuilder;
+import com.jayway.restassured.builder.ResponseSpecBuilder;
+import com.jayway.restassured.http.ContentType;
+import com.jayway.restassured.specification.RequestSpecification;
+import com.jayway.restassured.specification.ResponseSpecification;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.rates.RatesHelper;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+@SuppressWarnings({"rawtypes"})
+public class RatesTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @Before
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testRatesForLoans() {
+
+        // Retrieving all Rates
+        ArrayList<HashMap> allRatesData = RatesHelper.getRates(this.requestSpec, this.responseSpec);
+        Assert.assertNotNull(allRatesData);
+
+        // Testing Creation and Update of Loan Rate
+        final Integer loanRateId = RatesHelper.createRates(this.requestSpec, this.responseSpec,
+                RatesHelper.getLoanRateJSON());
+        Assert.assertNotNull(loanRateId);
+
+        //Update Rate percentage
+        HashMap changes = RatesHelper.updateRates(this.requestSpec, this.responseSpec, loanRateId,
+                RatesHelper.getModifyRateJSON());
+
+        HashMap rateDataAfterChanges = RatesHelper.getRateById(this.requestSpec, this.responseSpec, loanRateId);
+        Assert.assertEquals("Verifying Rate after modification", rateDataAfterChanges.get("percentage"), changes.get("percentage"));
+
+    }
+
+   
+}
diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/rates/RatesHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/rates/RatesHelper.java
new file mode 100644
index 0000000..43fd0d7
--- /dev/null
+++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/rates/RatesHelper.java
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests.common.rates;
+
+import com.google.gson.Gson;
+import com.jayway.restassured.specification.RequestSpecification;
+import com.jayway.restassured.specification.ResponseSpecification;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.Utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public class RatesHelper {
+
+    private static final String RATES_URL = "/fineract-provider/api/v1/rates";
+    private static final String CREATE_RATES_URL = RATES_URL + "?" + Utils.TENANT_IDENTIFIER;
+    private final static String PERCENTAGE = "10";
+    private final static Integer PRODUCT_APPLY_LOAN = 1;
+    private final static Boolean ACTIVE = true;
+
+    public static ArrayList<HashMap> getRates(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
+        return (ArrayList) Utils.performServerGet(requestSpec, responseSpec, RATES_URL + "?" + Utils.TENANT_IDENTIFIER, "");
+    }
+
+
+    public static Integer createRates(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+                                      final String request) {
+        return Utils.performServerPost(requestSpec, responseSpec, CREATE_RATES_URL, request, "resourceId");
+    }
+
+    public static HashMap getRateById(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+                                      final Integer rateId) {
+        return Utils.performServerGet(requestSpec, responseSpec, RATES_URL + "/" + rateId + "?" + Utils.TENANT_IDENTIFIER, "");
+    }
+
+    public static HashMap updateRates(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+                                      final Integer rateId, final String request) {
+        return Utils.performServerPut(requestSpec, responseSpec, RATES_URL + "/" + rateId + "?" + Utils.TENANT_IDENTIFIER, request,
+                CommonConstants.RESPONSE_CHANGES);
+    }
+
+    public static String getLoanRateJSON() {
+        return getLoanRateJSON(RatesHelper.PRODUCT_APPLY_LOAN, RatesHelper.PERCENTAGE);
+    }
+
+    public static String getLoanRateJSON(final Integer productApply, final String percentage) {
+        final HashMap<String, Object> map = populateDefaultsForLoan();
+        map.put("percentage", percentage);
+        map.put("productApply", productApply);
+        String crateRateJSON = new Gson().toJson(map);
+        return crateRateJSON;
+    }
+
+    public static HashMap<String, Object> populateDefaultsForLoan() {
+        final HashMap<String, Object> map = new HashMap<>();
+        map.put("active", RatesHelper.ACTIVE);
+        map.put("percentage", RatesHelper.PERCENTAGE);
+        map.put("locale", "en");
+        map.put("productApply", RatesHelper.PRODUCT_APPLY_LOAN);
+        map.put("name", Utils.randomNameGenerator("Rate_Loans_", 6));
+        return map;
+    }
+
+    public static String getModifyRateJSON() {
+        final HashMap<String, Object> map = new HashMap<>();
+        map.put("percentage", "15.0");
+        map.put("locale", "en");
+        String json = new Gson().toJson(map);
+        return json;
+    }
+
+}
+
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/data/RateData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/data/RateData.java
index d60c8dd..e249a1f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/data/RateData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/data/RateData.java
@@ -19,6 +19,8 @@
 
 package org.apache.fineract.portfolio.rate.data;
 
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+
 import java.io.Serializable;
 import java.math.BigDecimal;
 
@@ -33,17 +35,17 @@ public class RateData implements Serializable {
 
   private BigDecimal percentage;
 
-  private String productApply;
+  private EnumOptionData productApply;
 
   private boolean active;
 
   public static RateData instance(final Long id, final String name, final BigDecimal percentage,
-      final String productApply, final boolean active) {
+      final EnumOptionData productApply, final boolean active) {
     return new RateData(id, name, percentage, productApply, active);
   }
 
   private RateData(final Long id, final String name, final BigDecimal percentage,
-      final String productApply, final boolean active) {
+      final EnumOptionData productApply, final boolean active) {
     this.id = id;
     this.name = name;
     this.percentage = percentage;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/Rate.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/Rate.java
index 3d0803b..ad808f4 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/Rate.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/Rate.java
@@ -19,11 +19,18 @@
 
 package org.apache.fineract.portfolio.rate.domain;
 
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import org.apache.commons.lang.StringUtils;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.portfolio.charge.exception.ChargeParameterUpdateNotSupportedException;
 import org.apache.fineract.useradministration.domain.AppUser;
+import org.apache.fineract.portfolio.rate.domain.RateAppliesTo;
 
 import javax.persistence.*;
 import java.math.BigDecimal;
@@ -45,7 +52,7 @@ public class Rate extends AbstractAuditableCustom<AppUser, Long> {
   private BigDecimal percentage;
 
   @Column(name = "product_apply", length = 100)
-  private String productApply;
+  private Integer productApply;
 
   @Column(name = "active", nullable = false)
   private boolean active;
@@ -59,19 +66,19 @@ public class Rate extends AbstractAuditableCustom<AppUser, Long> {
   }
 
 
-  public Rate(String name, BigDecimal percentage, String productApply, boolean active,
+  public Rate(String name, BigDecimal percentage, RateAppliesTo productApply, boolean active,
       AppUser approveUser) {
     this.name = name;
     this.percentage = percentage;
-    this.productApply = productApply;
+    this.productApply = productApply.getValue();
     this.active = active;
     this.approveUser = approveUser;
   }
 
-  public Rate(String name, BigDecimal percentage, String productApply, boolean active) {
+  public Rate(String name, BigDecimal percentage, RateAppliesTo productApply, boolean active) {
     this.name = name;
     this.percentage = percentage;
-    this.productApply = productApply;
+    this.productApply = productApply.getValue();
     this.active = active;
   }
 
@@ -107,11 +114,11 @@ public class Rate extends AbstractAuditableCustom<AppUser, Long> {
     this.approveUser = approveUser;
   }
 
-  public String getProductApply() {
+  public Integer getProductApply() {
     return productApply;
   }
 
-  public void setProductApply(String productApply) {
+  public void setProductApply(Integer productApply) {
     this.productApply = productApply;
   }
 
@@ -126,7 +133,7 @@ public class Rate extends AbstractAuditableCustom<AppUser, Long> {
         '}';
   }
 
-  public static Rate from(String name, BigDecimal percentage, String productApply, Boolean active) {
+  public static Rate from(String name, BigDecimal percentage, RateAppliesTo productApply, Boolean active) {
     return new Rate(name, percentage, productApply, active);
   }
 
@@ -136,8 +143,8 @@ public class Rate extends AbstractAuditableCustom<AppUser, Long> {
 
     final BigDecimal percentage = command.bigDecimalValueOfParameterNamed("percentage");
 
-    final String productApply = command.stringValueOfParameterNamed("productApply");
-
+    final RateAppliesTo productApply = RateAppliesTo.fromInt(command.integerValueOfParameterNamed("productApply"));
+    
     final boolean active = command.booleanPrimitiveValueOfParameterNamed("active");
 
     return new Rate(name, percentage, productApply, active, user);
@@ -162,10 +169,9 @@ public class Rate extends AbstractAuditableCustom<AppUser, Long> {
     }
 
     final String productApplyParamName = "productApply";
-    if (command.isChangeInStringParameterNamed(productApplyParamName, this.productApply)) {
-      final String newValue = command.stringValueOfParameterNamed(productApplyParamName);
-      actualChanges.put(productApplyParamName, newValue);
-      this.productApply = StringUtils.defaultIfEmpty(newValue, null);
+    if (command.isChangeInIntegerParameterNamed(productApplyParamName, this.productApply)) {
+      final String errorMessage = "Update of Rate applies to is not supported";
+      throw new ChargeParameterUpdateNotSupportedException("rate.applies.to", errorMessage);
     }
 
     final String activeParamName = "active";
@@ -192,7 +198,7 @@ public class Rate extends AbstractAuditableCustom<AppUser, Long> {
     return approveUserId;
   }
 
-  public void assembleFrom(String name, BigDecimal percentage, String productApply, boolean active){
+  public void assembleFrom(String name, BigDecimal percentage, Integer productApply, boolean active){
     this.name = name;
     this.percentage = percentage;
     this.productApply = productApply;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateAppliesTo.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateAppliesTo.java
new file mode 100644
index 0000000..684e142
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateAppliesTo.java
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ 
+ package org.apache.fineract.portfolio.rate.domain;
+
+public enum RateAppliesTo {
+
+    INVALID(0, "rateAppliesTo.invalid"), 
+    LOAN(1, "rateAppliesTo.loan");
+    
+    private final Integer value;
+    private final String code;
+
+    private RateAppliesTo(final Integer value, final String code) {
+        this.value = value;
+        this.code = code;
+    }
+
+    public Integer getValue() {
+        return this.value;
+    }
+
+    public String getCode() {
+        return this.code;
+    }
+
+    public static RateAppliesTo fromInt(final Integer rateAppliesTo) {
+        RateAppliesTo rateAppliesToType = RateAppliesTo.INVALID;
+
+        if (rateAppliesTo != null) {
+            switch (rateAppliesTo) {
+                case 1:
+                    rateAppliesToType = LOAN;
+                break;
+                default:
+                    rateAppliesToType = INVALID;
+                break;
+            }
+        }
+
+        return rateAppliesToType;
+    }
+
+    public boolean isLoanRate() {
+        return this.value.equals(RateAppliesTo.LOAN.getValue());
+    }
+    public static Object[] validValues() {
+        return new Object[] { RateAppliesTo.LOAN.getValue() };
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateEnumerations.java
new file mode 100644
index 0000000..a1d79a8
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateEnumerations.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.portfolio.rate.service;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.portfolio.rate.domain.RateAppliesTo;
+
+public class RateEnumerations {
+
+    public static EnumOptionData rateAppliesTo(final int id) {
+        return rateAppliesTo(RateAppliesTo.fromInt(id));
+    }
+
+    public static EnumOptionData rateAppliesTo(final RateAppliesTo type) {
+        EnumOptionData optionData = null;
+        switch (type) {
+            case LOAN:
+                optionData = new EnumOptionData(RateAppliesTo.LOAN.getValue().longValue(), RateAppliesTo.LOAN.getCode(), "Loan");
+                break;
+            default:
+                optionData = new EnumOptionData(RateAppliesTo.INVALID.getValue().longValue(), RateAppliesTo.INVALID.getCode(),
+                        "Invalid");
+                break;
+        }
+        return optionData;
+    }
+
+ 
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadServiceImpl.java
index 44682de..f3e5e4f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadServiceImpl.java
@@ -19,8 +19,11 @@
 
 package org.apache.fineract.portfolio.rate.service;
 
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.charge.service.ChargeEnumerations;
+import org.apache.fineract.portfolio.rate.domain.RateAppliesTo;
 import org.apache.fineract.portfolio.rate.exception.RateNotFoundException;
 import org.apache.fineract.portfolio.rate.domain.Rate;
 import org.apache.fineract.portfolio.rate.data.RateData;
@@ -92,7 +95,7 @@ public class RateReadServiceImpl implements RateReadService {
     this.context.authenticatedUser();
     final RateMapper rm = new RateMapper();
     final String sql = "select " + rm.rateSchema() + " where r.active = ? and product_apply=?";
-    return this.jdbcTemplate.query(sql, rm, new Object[]{true, "m_loan"});
+    return this.jdbcTemplate.query(sql, rm, new Object[]{true, RateAppliesTo.LOAN.getValue() });
   }
 
   @Override
@@ -133,16 +136,17 @@ public class RateReadServiceImpl implements RateReadService {
       final Long id = resultSet.getLong("id");
       final String name = resultSet.getString("name");
       final BigDecimal percentage = resultSet.getBigDecimal("percentage");
-      final String productApply = resultSet.getString("productApply");
+      final Integer productApply = resultSet.getInt("productApply");
+      final EnumOptionData productAppliesTo = RateEnumerations.rateAppliesTo(productApply);
       final boolean active = resultSet.getBoolean("active");
-      return RateData.instance(id, name, percentage, productApply, active);
+      return RateData.instance(id, name, percentage, productAppliesTo, active);
     }
 
     public RateData mapRow(Rate rateResponse, int i) {
       final Long id = rateResponse.getId();
       final String name = rateResponse.getName();
       final BigDecimal percentage = rateResponse.getPercentage();
-      final String productApply = rateResponse.getProductApply();
+      final EnumOptionData productApply = RateEnumerations.rateAppliesTo(rateResponse.getProductApply());;
       final boolean active = rateResponse.isActive();
       return RateData.instance(id, name, percentage, productApply, active);
     }
diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V352__rates.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V352__rates.sql
index 1ca0986..37fe62f 100644
--- a/fineract-provider/src/main/resources/sql/migrations/core_db/V352__rates.sql
+++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V352__rates.sql
@@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS `m_rate` (
   `name` varchar(250) NOT NULL,
   `percentage` decimal(10,2) NOT NULL,
   `active` tinyint(1) DEFAULT '0',
-  `product_apply` varchar(100) NOT NULL,
+  `product_apply` smallint(5) NOT NULL,
   `created_date` datetime NULL DEFAULT NULL,
   `createdby_id` bigint(20) NOT NULL,
   `lastmodifiedby_id` bigint(20) NOT NULL,
@@ -63,5 +63,4 @@ INSERT INTO `m_permission`
 
 INSERT INTO `c_configuration`
 (`name`, `value`, `enabled`, `is_trap_door`, `description`) VALUES
-('vat-tax', 12, 0, 0, 'VAT tax'),
 ('sub-rates', 12, 0, 0, 'Enable Rates Module');;


[fineract] 01/02: A new rates module was added to define new rates that can be used to set min and max nominal interest rate, when a new loan account is created they can be used to determine which rates may be applicable for the loan account.

Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

angelboxes pushed a commit to branch Fineract-614
in repository https://gitbox.apache.org/repos/asf/fineract.git

commit 4e3d36c135444aed358e3ba6733855400b1937c0
Author: Angel Cajas <an...@bowpi.com>
AuthorDate: Mon Jul 30 16:52:17 2018 -0600

    A new rates module was added to define new rates that can be used to set min and max nominal interest rate, when a new loan account is created they can be used to determine which rates may be applicable for the loan account.
---
 .../commands/service/CommandWrapperBuilder.java    |  17 ++
 .../domain/ConfigurationDomainService.java         |   2 +
 .../domain/ConfigurationDomainServiceJpa.java      |  11 ++
 .../api/LoanTransactionsApiResource.java           |   6 +-
 .../loanaccount/api/LoansApiResource.java          |  63 ++++---
 .../loanaccount/data/LoanAccountData.java          |  71 ++++++--
 .../loanaccount/data/LoanChargePaidByData.java     |  15 ++
 .../loanaccount/data/LoanTransactionData.java      |  10 +
 .../portfolio/loanaccount/domain/Loan.java         |  40 +++-
 ...alculateLoanScheduleQueryFromApiJsonHelper.java |   3 +-
 .../LoanApplicationCommandFromApiJsonHelper.java   |   2 +-
 ...ationWritePlatformServiceJpaRepositoryImpl.java |  13 +-
 .../loanaccount/service/LoanAssembler.java         |  17 +-
 .../LoanChargePaidByReadPlatformService.java       |  27 +++
 .../LoanChargePaidByReadPlatformServiceImpl.java   |  82 +++++++++
 .../loanproduct/LoanProductConstants.java          |   7 +-
 .../loanproduct/api/LoanProductsApiResource.java   |  26 ++-
 .../loanproduct/data/LoanProductData.java          |  41 ++++-
 .../portfolio/loanproduct/domain/LoanProduct.java  |  51 +++++-
 .../domain/LoanProductMinMaxConstraints.java       |   8 +
 .../domain/LoanProductRelatedDetail.java           |   3 +
 .../serialization/LoanProductDataValidator.java    |   3 +-
 .../LoanProductReadPlatformServiceImpl.java        |  22 ++-
 ...oductWritePlatformServiceJpaRepositoryImpl.java |  40 +++-
 .../portfolio/rate/api/RateApiConstants.java       |  29 +++
 .../portfolio/rate/api/RateApiResource.java        | 137 ++++++++++++++
 .../fineract/portfolio/rate/data/RateData.java     |  53 ++++++
 .../fineract/portfolio/rate/domain/Rate.java       | 201 +++++++++++++++++++++
 .../portfolio/rate/domain/RateRepository.java      |  36 ++++
 .../rate/domain/RateRepositoryWrapper.java         |  70 +++++++
 .../rate/exception/RateAlreadyExistException.java  |  30 +++
 .../rate/exception/RateNotFoundException.java      |  35 ++++
 .../rate/handler/CreateRateCommandHandler.java     |  48 +++++
 .../rate/handler/UpdateRateCommandHandler.java     |  51 ++++++
 ...teDefinitionCommandFromApiJsonDeserializer.java | 129 +++++++++++++
 .../portfolio/rate/service/RateAssembler.java      |  80 ++++++++
 .../portfolio/rate/service/RateReadService.java    |  44 +++++
 .../rate/service/RateReadServiceImpl.java          | 150 +++++++++++++++
 .../portfolio/rate/service/RateWriteService.java   |  34 ++++
 .../rate/service/RateWriteServiceImpl.java         | 157 ++++++++++++++++
 .../sql/migrations/core_db/V352__rates.sql         |  67 +++++++
 41 files changed, 1846 insertions(+), 85 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index eb3fe6d..33ae031 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -3147,4 +3147,21 @@ public class CommandWrapperBuilder {
         this.href = "/self/pocket?command="+PocketApiConstants.delinkAccountsFromPocketCommandParam;
         return this;
     }
+
+    public CommandWrapperBuilder createRate() {
+        this.actionName = "CREATE";
+        this.entityName = "RATE";
+        this.entityId = null;
+        this.href = "/rates/template";
+        return this;
+    }
+
+    public CommandWrapperBuilder updateRate(final Long rateId) {
+        this.actionName = "UPDATE";
+        this.entityName = "RATE";
+        this.entityId = rateId;
+        this.href = "/rates/" + rateId;
+        return this;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
index f2eab64..d665a4f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
@@ -93,4 +93,6 @@ public interface ConfigurationDomainService {
     Integer retrieveOTPCharacterLength();
 
     Integer retrieveOTPLiveTime();
+
+    boolean isSubRatesEnabled();
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
index 3d51ddd..2094140 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
@@ -333,4 +333,15 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
         }
         return configurations.get(key);
     }
+
+    @Override
+    public boolean isSubRatesEnabled() {
+        GlobalConfigurationPropertyData configuration = getGlobalConfigurationPropertyData("sub-rates");
+        if (configuration==null){
+            return false;
+        }else{
+            return configuration.isEnabled();
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
index 6e45fd4..3aee369 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
@@ -32,6 +32,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
+import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
 import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
@@ -66,19 +67,21 @@ public class LoanTransactionsApiResource {
     private final DefaultToApiJsonSerializer<LoanTransactionData> toApiJsonSerializer;
     private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
     private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
+    private final LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService;
 
     @Autowired
     public LoanTransactionsApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
             final ApiRequestParameterHelper apiRequestParameterHelper,
             final DefaultToApiJsonSerializer<LoanTransactionData> toApiJsonSerializer,
             final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
-            PaymentTypeReadPlatformService paymentTypeReadPlatformService) {
+            PaymentTypeReadPlatformService paymentTypeReadPlatformService, LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService) {
         this.context = context;
         this.loanReadPlatformService = loanReadPlatformService;
         this.apiRequestParameterHelper = apiRequestParameterHelper;
         this.toApiJsonSerializer = toApiJsonSerializer;
         this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
         this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
+        this.loanChargePaidByReadPlatformService = loanChargePaidByReadPlatformService;
     }
 
     private boolean is(final String commandParam, final String commandValue) {
@@ -154,6 +157,7 @@ public class LoanTransactionsApiResource {
         this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
 
         LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransaction(loanId, transactionId);
+        transactionData.setLoanChargePaidByList(this.loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(transactionId));
         final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
         if (settings.isTemplate()) {
             final Collection<PaymentTypeData> paymentTypeOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
index 2849b9a..4d280e5 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
@@ -52,6 +52,7 @@ import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookP
 import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookService;
 import org.apache.fineract.infrastructure.codes.data.CodeValueData;
 import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
+import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
 import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
 import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
 import org.apache.fineract.infrastructure.core.api.JsonQuery;
@@ -112,6 +113,8 @@ import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatform
 import org.apache.fineract.portfolio.note.data.NoteData;
 import org.apache.fineract.portfolio.note.domain.NoteType;
 import org.apache.fineract.portfolio.note.service.NoteReadPlatformServiceImpl;
+import org.apache.fineract.portfolio.rate.data.RateData;
+import org.apache.fineract.portfolio.rate.service.RateReadService;
 import org.apache.fineract.portfolio.savings.DepositAccountType;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -142,7 +145,7 @@ public class LoansApiResource {
             "syncDisbursementWithMeeting", "loanCounter", "loanProductCounter", "notes", "accountLinkingOptions", "linkedAccount",
             "interestRateDifferential", "isFloatingInterestRate", "interestRatesPeriods", LoanApiConstants.canUseForTopup,
             LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount, LoanApiConstants.clientActiveLoanOptions,
-            LoanApiConstants.datatables));
+            LoanApiConstants.datatables, LoanProductConstants.ratesParamName));
 
     private final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount"));
     private final String resourceNameForPermissions = "LOAN";
@@ -174,30 +177,32 @@ public class LoansApiResource {
     private final EntityDatatableChecksReadService entityDatatableChecksReadService;
     private final BulkImportWorkbookService bulkImportWorkbookService;
     private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
-
+    private final RateReadService rateReadService;
+    private final ConfigurationDomainService configurationDomainService;
 
     @Autowired
     public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
-            final LoanProductReadPlatformService loanProductReadPlatformService,
-            final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
-            final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService,
-            final CollateralReadPlatformService loanCollateralReadPlatformService,
-            final LoanScheduleCalculationPlatformService calculationPlatformService,
-            final GuarantorReadPlatformService guarantorReadPlatformService,
-            final CodeValueReadPlatformService codeValueReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
-            final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
-            final DefaultToApiJsonSerializer<LoanApprovalData> loanApprovalDataToApiJsonSerializer,
-            final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
-            final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper,
-            final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
-            final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
-            final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
-            final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
-            final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
-            final AccountDetailsReadPlatformService accountDetailsReadPlatformService,
-            final EntityDatatableChecksReadService entityDatatableChecksReadService,
-            final BulkImportWorkbookService bulkImportWorkbookService,
-            final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService) {
+        final LoanProductReadPlatformService loanProductReadPlatformService,
+        final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
+        final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService,
+        final CollateralReadPlatformService loanCollateralReadPlatformService,
+        final LoanScheduleCalculationPlatformService calculationPlatformService,
+        final GuarantorReadPlatformService guarantorReadPlatformService,
+        final CodeValueReadPlatformService codeValueReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
+        final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
+        final DefaultToApiJsonSerializer<LoanApprovalData> loanApprovalDataToApiJsonSerializer,
+        final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
+        final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper,
+        final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
+        final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
+        final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
+        final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
+        final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
+        final AccountDetailsReadPlatformService accountDetailsReadPlatformService,
+        final EntityDatatableChecksReadService entityDatatableChecksReadService,
+        final BulkImportWorkbookService bulkImportWorkbookService,
+        final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService, final RateReadService rateReadService, 
+        final ConfigurationDomainService configurationDomainService) {
         this.context = context;
         this.loanReadPlatformService = loanReadPlatformService;
         this.loanProductReadPlatformService = loanProductReadPlatformService;
@@ -223,8 +228,10 @@ public class LoansApiResource {
         this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
         this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
         this.entityDatatableChecksReadService = entityDatatableChecksReadService;
+        this.rateReadService = rateReadService;
         this.bulkImportWorkbookService=bulkImportWorkbookService;
         this.bulkImportWorkbookPopulatorService=bulkImportWorkbookPopulatorService;
+        this.configurationDomainService = configurationDomainService;
     }
 
     /*
@@ -280,6 +287,7 @@ public class LoansApiResource {
         LoanAccountData newLoanAccount = null;
         Long officeId = null;
         Collection<PortfolioAccountData> accountLinkingOptions = null;
+        boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled();
 
         if (productId != null) {
             newLoanAccount = this.loanReadPlatformService.retrieveLoanProductDetailsTemplate(productId, clientId, groupId);
@@ -357,7 +365,7 @@ public class LoansApiResource {
             // add product options, allowed loan officers and calendar options
             // (calendar options will be null in individual loan)
             newLoanAccount = LoanAccountData.associationsAndTemplate(newLoanAccount, productOptions, allowedLoanOfficers, calendarOptions,
-                    accountLinkingOptions);
+                    accountLinkingOptions, isRatesEnabled);
         }
         final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService
                 .retrieveTemplates(StatusEnum.CREATE.getCode().longValue(), EntityTables.LOAN.getName(), productId);
@@ -619,13 +627,20 @@ public class LoansApiResource {
 
         paidInAdvanceTemplate = this.loanReadPlatformService.retrieveTotalPaidInAdvance(loanId);
 
+        //Get rates from Loan
+        boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled();
+        List<RateData> rates = null;
+        if (isRatesEnabled){
+          rates = this.rateReadService.retrieveLoanRates(loanId);
+        }
+
         final LoanAccountData loanAccount = LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, loanRepayments,
                 charges, collateral, guarantors, meeting, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
                 repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDayOfWeekTypeOptions, repaymentStrategyOptions, 
                 interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, 
                 fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions, loanCollateralOptions, 
                 calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
-                overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions);
+                overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled);
 
         final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
                 mandatoryResponseParameters);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
index 4f88ada..3b37745 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
@@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrat
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
 import org.apache.fineract.portfolio.note.data.NoteData;
+import org.apache.fineract.portfolio.rate.data.RateData;
 import org.joda.time.LocalDate;
 import org.springframework.util.CollectionUtils;
 
@@ -216,6 +217,10 @@ public class LoanAccountData {
     private List<DatatableData> datatables = null;
     private final Boolean isEqualAmortization;
 
+    //Rate
+    private final List<RateData> rates;
+    private final Boolean isRatesEnabled;
+
     //import fields
     private String dateFormat;
     private String locale;
@@ -308,6 +313,7 @@ public class LoanAccountData {
         this.groupId=groupId;
         this.expectedDisbursementDate=expectedDisbursementDate;
         this.charges = charges;
+        this.rates=null;
         this.id = null;
         this.accountNo = null;
 
@@ -415,6 +421,7 @@ public class LoanAccountData {
         this.minimumGap = null;
         this.maximumGap = null;
         this.isEqualAmortization = null;
+        this.isRatesEnabled = false;
     }
 
 
@@ -577,6 +584,9 @@ public class LoanAccountData {
         final String closureLoanAccountNo = null;
         final BigDecimal topupAmount = null;
         final boolean isEqualAmortization = false;
+        final List<RateData> rates = null;
+        final Boolean isRatesEnabled = false;
+
         return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
                 loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
                 loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
@@ -595,7 +605,7 @@ public class LoanAccountData {
                 maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
                 isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
                 createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
-                maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
+                maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled);
 
     }
 
@@ -717,6 +727,9 @@ public class LoanAccountData {
         final String closureLoanAccountNo = null;
         final BigDecimal topupAmount = null;
         final boolean isEqualAmortization = false;
+        final List<RateData> rates = null;
+        final Boolean isRatesEnabled = false;
+
         return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
                 loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
                 loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
@@ -735,7 +748,8 @@ public class LoanAccountData {
                 maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
                 isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
                 createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
-                maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
+                maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
+                rates, isRatesEnabled);
 
     }
 
@@ -765,7 +779,8 @@ public class LoanAccountData {
                 acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
                 acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
                 acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
-                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
+                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization,
+                acc.rates, acc.isRatesEnabled);
     }
 
     /**
@@ -888,6 +903,9 @@ public class LoanAccountData {
         final String closureLoanAccountNo = null;
         final BigDecimal topupAmount = null;
         final boolean isEqualAmortization = false;
+        final List<RateData> rates = null;
+        final Boolean isRatesEnabled = false;
+
         return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
                 loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
                 loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
@@ -906,7 +924,8 @@ public class LoanAccountData {
                 maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
                 isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
                 createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
-                maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
+                maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
+                rates, isRatesEnabled);
 
     }
 
@@ -936,7 +955,8 @@ public class LoanAccountData {
                 acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
                 acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
                 acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
-                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
+                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization,
+                acc.rates, acc.isRatesEnabled);
 
     }
 
@@ -1066,6 +1086,9 @@ public class LoanAccountData {
         final Long closureLoanId = null;
         final String closureLoanAccountNo = null;
         final BigDecimal topupAmount = null;
+        final List<RateData> rates = null;
+        final Boolean isRatesEnabled = false;
+
         return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
                 loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(),
                 product.getFundId(), product.getFundName(), loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -1090,7 +1113,7 @@ public class LoanAccountData {
                 originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
                 product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
                 product.getMaximumGapBetweenInstallments(), subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId,
-                closureLoanAccountNo, topupAmount, product.isEqualAmortization());
+                closureLoanAccountNo, topupAmount, product.isEqualAmortization(),rates, isRatesEnabled);
     }
 
     public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
@@ -1150,7 +1173,8 @@ public class LoanAccountData {
                 product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement,
                 paidInAdvance, acc.interestRatesPeriods, product.isVariableInstallmentsAllowed(),
                 product.getMinimumGapBetweenInstallments(), product.getMaximumGapBetweenInstallments(), acc.subStatus, acc.canUseForTopup,
-                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization());
+                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization(), acc.rates,
+                acc.isRatesEnabled);
 
     }
 
@@ -1219,6 +1243,8 @@ public class LoanAccountData {
         final PaidInAdvanceData paidInAdvance = null;
         final Collection<InterestRatePeriodData> interestRatesPeriods = null;
         final Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
+        final List<RateData> rates = null;
+        final Boolean isRatesEnabled = false;
 
         return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
                 loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -1238,7 +1264,8 @@ public class LoanAccountData {
                 outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
                 isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
                 createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
-                maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
+                maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
+                rates, isRatesEnabled);
     }
 
     /*
@@ -1262,7 +1289,7 @@ public class LoanAccountData {
             final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
             final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges,
             final PaidInAdvanceData paidInAdvance, Collection<InterestRatePeriodData> interestRatesPeriods,
-            final Collection<LoanAccountSummaryData> clientActiveLoanOptions) {
+            final Collection<LoanAccountSummaryData> clientActiveLoanOptions,final List<RateData> rates, final Boolean isRatesEnabled) {
         LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
         if (acc.product != null) {
             loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes();
@@ -1291,12 +1318,12 @@ public class LoanAccountData {
                 acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
                 acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed,
                 acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, clientActiveLoanOptions, acc.isTopup,
-                acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
+                acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, rates, isRatesEnabled);
     }
 
     public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
             final Collection<StaffData> allowedLoanOfficers, final Collection<CalendarData> calendarOptions,
-            final Collection<PortfolioAccountData> accountLinkingOptions) {
+            final Collection<PortfolioAccountData> accountLinkingOptions, final Boolean isRatesEnabled) {
         return associationsAndTemplate(acc, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors,
                 acc.meeting, productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions,
                 acc.repaymentFrequencyNthDayTypeOptions, acc.repaymentFrequencyDaysOfWeekTypeOptions,
@@ -1304,7 +1331,7 @@ public class LoanAccountData {
                 acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
                 allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
                 acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance,
-                acc.interestRatesPeriods, acc.clientActiveLoanOptions);
+                acc.interestRatesPeriods, acc.clientActiveLoanOptions,acc.rates, isRatesEnabled);
     }
 
     public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
@@ -1333,7 +1360,8 @@ public class LoanAccountData {
                 acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
                 acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
                 acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
-                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
+                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates,
+                acc.isRatesEnabled);
     }
 
     public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
@@ -1398,7 +1426,8 @@ public class LoanAccountData {
                 acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
                 acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
                 acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
-                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
+                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates,
+                acc.isRatesEnabled);
 
     }
 
@@ -1432,7 +1461,8 @@ public class LoanAccountData {
                 acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
                 acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
                 acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
-                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
+                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates,
+                acc.isRatesEnabled);
     }
 
     public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
@@ -1460,7 +1490,8 @@ public class LoanAccountData {
                 acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
                 acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
                 acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
-                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
+                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization,
+                acc.rates, acc.isRatesEnabled);
     }
 
     public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
@@ -1489,7 +1520,8 @@ public class LoanAccountData {
                 acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
                 acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
                 acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
-                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
+                acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization,
+                acc.rates, acc.isRatesEnabled);
     }
 
     private LoanAccountData(final Long id, //
@@ -1539,7 +1571,8 @@ public class LoanAccountData {
             final Collection<InterestRatePeriodData> interestRatesPeriods, final Boolean isVariableInstallmentsAllowed,
             final Integer minimumGap, final Integer maximumGap, final EnumOptionData subStatus, final Boolean canUseForTopup,
             final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final boolean isTopup,
-            final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization) {
+            final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization,
+            final List<RateData> rates, final Boolean isRatesEnabled) {
 
         this.id = id;
         this.accountNo = accountNo;
@@ -1619,6 +1652,7 @@ public class LoanAccountData {
         this.amortizationTypeOptions = amortizationTypeOptions;
         this.interestTypeOptions = interestTypeOptions;
         this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
+        this.isRatesEnabled = isRatesEnabled;
 
         if (CollectionUtils.isEmpty(transactionProcessingStrategyOptions)) {
             this.transactionProcessingStrategyOptions = null;
@@ -1722,6 +1756,7 @@ public class LoanAccountData {
         this.closureLoanAccountNo = closureLoanAccountNo;
         this.topupAmount = topupAmount;
         this.isEqualAmortization = isEqualAmortization;
+        this.rates = rates;
 
     }
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargePaidByData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargePaidByData.java
index 9978bcd..3545b6c 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargePaidByData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargePaidByData.java
@@ -27,6 +27,17 @@ public class LoanChargePaidByData {
     private final Integer installmentNumber;
     private final Long chargeId;
     private final Long transactionId;
+    private final String name;
+
+    public LoanChargePaidByData(final Long id, final BigDecimal amount, final Integer installmentNumber, final Long chargeId,
+        final Long transactionId, String name) {
+        this.id = id;
+        this.amount = amount;
+        this.installmentNumber = installmentNumber;
+        this.chargeId = chargeId;
+        this.transactionId = transactionId;
+        this.name=name;
+    }
 
     public LoanChargePaidByData(final Long id, final BigDecimal amount, final Integer installmentNumber, final Long chargeId,
             final Long transactionId) {
@@ -35,6 +46,7 @@ public class LoanChargePaidByData {
         this.installmentNumber = installmentNumber;
         this.chargeId = chargeId;
         this.transactionId = transactionId;
+        this.name=null;
     }
 
     public Long getId() {
@@ -57,4 +69,7 @@ public class LoanChargePaidByData {
         return this.transactionId;
     }
 
+    public String getName() {
+        return name;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
index 7b65017..1768a0e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
@@ -60,6 +60,8 @@ public class LoanTransactionData {
     private final boolean manuallyReversed;
 	private final LocalDate possibleNextRepaymentDate;
 
+    private Collection<LoanChargePaidByData> loanChargePaidByList;
+
     // templates
     final Collection<PaymentTypeData> paymentTypeOptions;
     
@@ -340,4 +342,12 @@ public class LoanTransactionData {
     public void setWriteOffReasonOptions(Collection<CodeValueData> writeOffReasonOptions){
     	this.writeOffReasonOptions =writeOffReasonOptions;
     }
+
+    public Collection<LoanChargePaidByData> getLoanChargePaidByList() {
+        return loanChargePaidByList;
+    }
+
+    public void setLoanChargePaidByList(Collection<LoanChargePaidByData> loanChargePaidByList) {
+        this.loanChargePaidByList = loanChargePaidByList;
+    }
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index c7b6cc3..263d42f 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -42,7 +42,9 @@ import javax.persistence.Embedded;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
 import javax.persistence.ManyToOne;
+import javax.persistence.ManyToMany;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.OrderBy;
@@ -132,6 +134,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessin
 import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
 import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
 import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
+import org.apache.fineract.portfolio.rate.domain.Rate;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.joda.time.Days;
 import org.joda.time.LocalDate;
@@ -392,20 +395,24 @@ public class Loan extends AbstractPersistableCustom<Long> {
     @OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch=FetchType.EAGER)
     private LoanTopupDetails loanTopupDetails;
 
+    @OneToMany(fetch = FetchType.LAZY)
+    @JoinTable(name = "m_loan_rate", joinColumns = @JoinColumn(name = "loan_id"), inverseJoinColumns = @JoinColumn(name = "rate_id"))
+    private List<Rate> rates;
+
     public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
             final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
             final LoanTransactionProcessingStrategy transactionProcessingStrategy,
             final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
             final Set<LoanCollateral> collateral, final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
             final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
-            final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential) {
+            final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates) {
         final LoanStatus status = null;
         final Group group = null;
         final Boolean syncDisbursementWithMeeting = null;
         return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
                 disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
-                interestRateDifferential);
+                interestRateDifferential, rates);
     }
 
     public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
@@ -415,13 +422,13 @@ public class Loan extends AbstractPersistableCustom<Long> {
             final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
             final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
             final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
-            final BigDecimal interestRateDifferential) {
+            final BigDecimal interestRateDifferential, final List<Rate> rates) {
         final LoanStatus status = null;
         final Client client = null;
         return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
                 disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
-                interestRateDifferential);
+                interestRateDifferential, rates);
     }
 
     public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
@@ -431,12 +438,12 @@ public class Loan extends AbstractPersistableCustom<Long> {
             final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
             final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
             final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
-            final BigDecimal interestRateDifferential) {
+            final BigDecimal interestRateDifferential, final List<Rate> rates) {
         final LoanStatus status = null;
         return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
                 disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
-                interestRateDifferential);
+                interestRateDifferential,rates);
     }
 
     protected Loan() {
@@ -449,7 +456,7 @@ public class Loan extends AbstractPersistableCustom<Long> {
             final Set<LoanCharge> loanCharges, final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting,
             final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
             final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
-            final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential) {
+            final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates) {
 
         this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
         this.loanRepaymentScheduleDetail.validateRepaymentPeriodWithGraceSettings();
@@ -507,6 +514,9 @@ public class Loan extends AbstractPersistableCustom<Long> {
 
         this.proposedPrincipal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount();
 
+        //rates added here
+        this.rates = rates;
+
     }
 
     private LoanSummary updateSummaryWithTotalFeeChargesDueAtDisbursement(final BigDecimal feeChargesDueAtDisbursement) {
@@ -1162,6 +1172,14 @@ public class Loan extends AbstractPersistableCustom<Long> {
         this.collateral.addAll(associateWithThisLoan(loanCollateral));
     }
 
+    public void updateLoanRates(final List<Rate> loanRates) {
+        if (this.rates == null) {
+            this.rates = new ArrayList<>();
+        }
+        this.rates.clear();
+        this.rates.addAll(loanRates);
+    }
+
     public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule, AppUser currentUser) {
         this.repaymentScheduleInstallments.clear();
         for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
@@ -6528,4 +6546,12 @@ public class Loan extends AbstractPersistableCustom<Long> {
     }
     
     public boolean isIndividualLoan(){return AccountType.fromInt(this.loanType).isIndividualAccount();}
+
+    public void setRates(List<Rate> rates) {
+        this.rates = rates;
+    }
+
+    public List<Rate> getRates() {
+        return rates;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java
index 3a8fe38..480b4d8 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java
@@ -69,7 +69,8 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
             LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
             LoanApiConstants.isFloatingInterestRateParameterName, LoanApiConstants.interestRateDifferentialParameterName,
             LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName,
-            LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam));
+            LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam,
+            LoanProductConstants.ratesParamName));
 
     private final FromJsonHelper fromApiJsonHelper;
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
index 466ec01..e6388ea 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
@@ -93,7 +93,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
             LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName,
             LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
             LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
-            LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam));
+            LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam, LoanProductConstants.ratesParamName));
 
     private final FromJsonHelper fromApiJsonHelper;
     private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
index 45169df..dbb1ffe 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
@@ -96,6 +96,7 @@ import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundEx
 import org.apache.fineract.portfolio.loanproduct.serialization.LoanProductDataValidator;
 import org.apache.fineract.portfolio.note.domain.Note;
 import org.apache.fineract.portfolio.note.domain.NoteRepository;
+import org.apache.fineract.portfolio.rate.service.RateAssembler;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
 import org.apache.fineract.useradministration.domain.AppUser;
@@ -151,6 +152,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
     private final FineractEntityToEntityMappingRepository repository;
     private final FineractEntityRelationRepository fineractEntityRelationRepository;
     private final LoanProductReadPlatformService loanProductReadPlatformService;
+    private final RateAssembler rateAssembler;
 
     @Autowired
     public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
@@ -173,7 +175,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
             final LoanScheduleAssembler loanScheduleAssembler, final LoanUtilService loanUtilService, 
             final CalendarReadPlatformService calendarReadPlatformService, final GlobalConfigurationRepositoryWrapper globalConfigurationRepository,
             final FineractEntityToEntityMappingRepository repository, final FineractEntityRelationRepository fineractEntityRelationRepository,
-            final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, final LoanProductReadPlatformService loanProductReadPlatformService) {
+            final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, final LoanProductReadPlatformService loanProductReadPlatformService,
+            final RateAssembler rateAssembler) {
         this.context = context;
         this.fromJsonHelper = fromJsonHelper;
         this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
@@ -209,7 +212,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
         this.repository = repository;
         this.fineractEntityRelationRepository = fineractEntityRelationRepository;
         this.loanProductReadPlatformService = loanProductReadPlatformService;
-
+        this.rateAssembler = rateAssembler;
     }
 
     private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -831,6 +834,12 @@ public void checkForProductMixRestrictions(final Loan loan) {
                 existingLoanApplication.recalculateAllCharges();
             }
 
+            //Changes to modify loan rates.
+            if (command.hasParameter(LoanProductConstants.ratesParamName)) {
+                existingLoanApplication.updateLoanRates(rateAssembler.fromParsedJson(command.parsedJson()));
+            }
+
+
             this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(),
                     existingLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
                     productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java
index 95fc3c0..a727170 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 
+import com.google.gson.JsonArray;
 import org.apache.fineract.infrastructure.codes.domain.CodeValue;
 import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
 import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
@@ -71,6 +72,7 @@ import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementData
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
+import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
@@ -78,6 +80,8 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessin
 import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException;
 import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
 import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
+import org.apache.fineract.portfolio.rate.domain.Rate;
+import org.apache.fineract.portfolio.rate.service.RateAssembler;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -106,6 +110,7 @@ public class LoanAssembler {
     private final ConfigurationDomainService configurationDomainService;
     private final WorkingDaysRepositoryWrapper workingDaysRepository;
     private final LoanUtilService loanUtilService;
+    private final RateAssembler rateAssembler;
 
     @Autowired
     public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanRepositoryWrapper loanRepository,
@@ -117,7 +122,7 @@ public class LoanAssembler {
             final CollateralAssembler loanCollateralAssembler, final LoanSummaryWrapper loanSummaryWrapper,
             final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
             final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService,
-            final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanUtilService loanUtilService) {
+            final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanUtilService loanUtilService, RateAssembler rateAssembler) {
         this.fromApiJsonHelper = fromApiJsonHelper;
         this.loanRepository = loanRepository;
         this.loanProductRepository = loanProductRepository;
@@ -136,6 +141,7 @@ public class LoanAssembler {
         this.configurationDomainService = configurationDomainService;
         this.workingDaysRepository = workingDaysRepository;
         this.loanUtilService = loanUtilService;
+        this.rateAssembler = rateAssembler;
     }
 
     public Loan assembleFrom(final Long accountId) {
@@ -224,6 +230,9 @@ public class LoanAssembler {
         Client client = null;
         Group group = null;
 
+        //Here we add Rates to LoanApplication
+        final List<Rate> rates = this.rateAssembler.fromParsedJson(element);
+
         final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element);
         
         final BigDecimal interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.interestRateDifferentialParameterName, element);
@@ -252,21 +261,21 @@ public class LoanAssembler {
             loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(),
                     loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges,
                     collateral, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
-                    createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential);
+                    createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates);
 
         } else if (group != null) {
 
             loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
                     loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
                     syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
-                    createStandingInstructionAtDisbursement,isFloatingInterestRate, interestRateDifferential);
+                    createStandingInstructionAtDisbursement,isFloatingInterestRate, interestRateDifferential, rates);
 
         } else if (client != null) {
 
             loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund,
                     loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
                     fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement,
-                    isFloatingInterestRate, interestRateDifferential);
+                    isFloatingInterestRate, interestRateDifferential, rates);
 
         }
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformService.java
new file mode 100644
index 0000000..1ca6dc1
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformService.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import java.util.List;
+import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
+
+public interface LoanChargePaidByReadPlatformService {
+
+    List<LoanChargePaidByData> getLoanChargesPaidByTransactionId(Long id);
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformServiceImpl.java
new file mode 100644
index 0000000..60cafe0
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformServiceImpl.java
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@Service
+public class LoanChargePaidByReadPlatformServiceImpl implements
+    LoanChargePaidByReadPlatformService {
+
+  private final JdbcTemplate jdbcTemplate;
+  private final PlatformSecurityContext context;
+
+  @Autowired
+  public LoanChargePaidByReadPlatformServiceImpl(final PlatformSecurityContext context,
+      final RoutingDataSource dataSource) {
+    this.context = context;
+    this.jdbcTemplate = new JdbcTemplate(dataSource);
+
+  }
+
+  @Override
+  public List<LoanChargePaidByData> getLoanChargesPaidByTransactionId(Long transactionId) {
+    this.context.authenticatedUser();
+    final LoanChargePaidByMapper rm = new LoanChargePaidByMapper();
+    final String sql = "select " + rm.loanChargePaidBySchema() + " where lcpd.loan_transaction_id = ?";
+    return this.jdbcTemplate.query(sql, rm, new Object[]{transactionId});
+  }
+
+  private static final class LoanChargePaidByMapper implements RowMapper<LoanChargePaidByData> {
+
+    public String loanChargePaidBySchema() {
+      return "lcpd.id as id, lcpd.amount as amount, lcpd.installment_number as installmentNumber," +
+          " lcpd.loan_charge_id as chargeId, lcpd.loan_transaction_id as transactionId, " +
+          " c.name as chargeName"
+          + " from m_loan_charge_paid_by lcpd"
+          + " join m_loan_charge lc on lc.id=lcpd.loan_charge_Id"
+          + " join m_charge c on c.id=lc.charge_id";
+    }
+
+    @Override
+    public LoanChargePaidByData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum)
+        throws SQLException {
+      final Long id = rs.getLong("id");
+      final BigDecimal amount = rs.getBigDecimal("amount");
+      final Integer installmentNumber = rs.getInt("installmentNumber");
+      final Long chargeId = rs.getLong("chargeId");
+      final Long transactionId = rs.getLong("transactionId");
+      final String chargeName = rs.getString("chargeName");
+      return new LoanChargePaidByData(id, amount, installmentNumber, chargeId, transactionId, chargeName);
+    }
+
+  }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
index 317f7f4..7ab9dbf 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
@@ -131,8 +131,11 @@ public interface LoanProductConstants {
     public static final String allowPartialPeriodInterestCalcualtionParamName = "allowPartialPeriodInterestCalcualtion";
 
     public static final String canUseForTopup = "canUseForTopup";
-    
+
+    //Rate Module
+    public static final String ratesParamName = "rates";
+
     public static final String isEqualAmortizationParam = "isEqualAmortization";
-    
+
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
index 9c68524..7889257 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
@@ -27,6 +27,7 @@ import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToG
 import org.apache.fineract.commands.domain.CommandWrapper;
 import org.apache.fineract.commands.service.CommandWrapperBuilder;
 import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
 import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
 import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
@@ -52,6 +53,8 @@ import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatfor
 import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
 import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
+import org.apache.fineract.portfolio.rate.data.RateData;
+import org.apache.fineract.portfolio.rate.service.RateReadService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
@@ -80,7 +83,8 @@ public class LoanProductsApiResource {
             "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions",
             "accountingRuleOptions", "accountingMappingOptions", "floatingRateOptions", "isLinkedToFloatingInterestRates",
             "floatingRatesId", "interestRateDifferential", "minDifferentialLendingRate", "defaultDifferentialLendingRate",
-            "maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed", LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam));
+            "maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed", LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam,
+            LoanProductConstants.ratesParamName));
 
     private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
             "allowedProducts", "productOptions"));
@@ -103,6 +107,9 @@ public class LoanProductsApiResource {
     private final DropdownReadPlatformService commonDropdownReadPlatformService;
     private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
     private final FloatingRatesReadPlatformService floatingRateReadPlatformService;
+    private final RateReadService rateReadService;
+    private final ConfigurationDomainService configurationDomainService;
+
 
     @Autowired
     public LoanProductsApiResource(final PlatformSecurityContext context, final LoanProductReadPlatformService readPlatformService,
@@ -117,7 +124,8 @@ public class LoanProductsApiResource {
             final ProductMixReadPlatformService productMixReadPlatformService,
             final DropdownReadPlatformService commonDropdownReadPlatformService,
             PaymentTypeReadPlatformService paymentTypeReadPlatformService,
-            final FloatingRatesReadPlatformService floatingRateReadPlatformService) {
+            final FloatingRatesReadPlatformService floatingRateReadPlatformService, final RateReadService rateReadService,
+            final ConfigurationDomainService configurationDomainService) {
         this.context = context;
         this.loanProductReadPlatformService = readPlatformService;
         this.chargeReadPlatformService = chargeReadPlatformService;
@@ -134,6 +142,8 @@ public class LoanProductsApiResource {
         this.commonDropdownReadPlatformService = commonDropdownReadPlatformService;
         this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
         this.floatingRateReadPlatformService = floatingRateReadPlatformService;
+        this.rateReadService = rateReadService;
+        this.configurationDomainService = configurationDomainService;
     }
 
     @POST
@@ -266,6 +276,12 @@ public class LoanProductsApiResource {
             penaltyOptions = null;
         }
 
+        boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled();
+        Collection<RateData> rateOptions = this.rateReadService.retrieveLoanApplicableRates();
+        if(rateOptions.isEmpty()){
+            rateOptions = null;
+        }
+
         final Collection<CurrencyData> currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies();
         final List<EnumOptionData> amortizationTypeOptions = this.dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
         final List<EnumOptionData> interestTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
@@ -309,11 +325,11 @@ public class LoanProductsApiResource {
 
         return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions,
                 amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions,
-                interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions,
+                interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, rateOptions, accountOptions,
                 accountingRuleTypeOptions, loanCycleValueConditionTypeOptions, daysInMonthTypeOptions, daysInYearTypeOptions,
                 interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions,
-                preCloseInterestCalculationStrategyOptions, floatingRateOptions, interestRecalculationNthDayTypeOptions, 
-                interestRecalculationDayOfWeekTypeOptions);
+                preCloseInterestCalculationStrategyOptions, floatingRateOptions, interestRecalculationNthDayTypeOptions,
+                interestRecalculationDayOfWeekTypeOptions, isRatesEnabled);
     }
 
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
index 9d0af8b..bdd3602 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
@@ -46,6 +46,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
 import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
+import org.apache.fineract.portfolio.rate.data.RateData;
 import org.joda.time.LocalDate;
 import org.springframework.util.CollectionUtils;
 
@@ -132,6 +133,10 @@ public class LoanProductData {
     private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
     private Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
 
+    // rates
+    private final boolean isRatesEnabled;
+    private final Collection<RateData> rates;
+
     // template related
     private final Collection<FundData> fundOptions;
     @SuppressWarnings("unused")
@@ -145,6 +150,7 @@ public class LoanProductData {
     private final List<EnumOptionData> interestCalculationPeriodTypeOptions;
     private final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions;
     private final Collection<ChargeData> chargeOptions;
+    private final Collection<RateData> rateOptions;
     @SuppressWarnings("unused")
     private final Collection<ChargeData> penaltyOptions;
     @SuppressWarnings("unused")
@@ -260,6 +266,9 @@ public class LoanProductData {
         final boolean syncExpectedWithDisbursementDate = false;
         final boolean canUseForTopup = false;
         final boolean isEqualAmortization = false;
+        final Collection<RateData> rateOptions = null;
+        final Collection<RateData> rates = null;
+        final boolean isRatesEnabled= false;
         return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
                 minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType,
@@ -274,7 +283,7 @@ public class LoanProductData {
                 loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
                 interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
                 isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
+                syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
 
     }
 
@@ -352,6 +361,9 @@ public class LoanProductData {
         final boolean syncExpectedWithDisbursementDate = false;
         final boolean canUseForTopup = false;
         final boolean isEqualAmortization = false;
+        final Collection<RateData> rateOptions = null;
+        final Collection<RateData> rates = null;
+        final boolean isRatesEnabled = false;
 
         return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -367,7 +379,7 @@ public class LoanProductData {
                 loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
                 interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
                 isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
+                syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
 
     }
 
@@ -452,6 +464,9 @@ public class LoanProductData {
         final boolean syncExpectedWithDisbursementDate = false;
         final boolean canUseForTopup = false;
         final boolean isEqualAmortization = false;
+        final Collection<RateData> rateOptions = null;
+        final Collection<RateData> rates = null;
+        final boolean isRatesEnabled = false;
 
         return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -467,7 +482,7 @@ public class LoanProductData {
                 installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
                 floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
                 maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
+                syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
 
     }
     
@@ -546,6 +561,9 @@ public class LoanProductData {
         final boolean syncExpectedWithDisbursementDate = false;
         final boolean canUseForTopup = false;
         final boolean isEqualAmortization = false;
+        final Collection<RateData> rateOptions = null;
+        final Collection<RateData> rates = null;
+        final boolean isRatesEnabled = false;
 
         return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -561,7 +579,7 @@ public class LoanProductData {
                 installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
                 floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
                 maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
+                syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
 
     }
 
@@ -599,8 +617,9 @@ public class LoanProductData {
             boolean isLinkedToFloatingInterestRates, Integer floatingRateId, String floatingRateName, BigDecimal interestRateDifferential,
             BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate,
             boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed,
-            final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments, 
-            final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization) {
+            final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
+            final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization, Collection<RateData> rateOptions, Collection<RateData> rates,
+            final boolean isRatesEnabled) {
         this.id = id;
         this.name = name;
         this.shortName = shortName;
@@ -652,6 +671,9 @@ public class LoanProductData {
         this.status = status;
         this.externalId = externalId;
         this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment;
+        this.rateOptions = rateOptions;
+        this.rates = rates;
+        this.isRatesEnabled = isRatesEnabled;
 
         this.chargeOptions = null;
         this.penaltyOptions = null;
@@ -714,13 +736,13 @@ public class LoanProductData {
             final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions,
             final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions,
             final Collection<FundData> fundOptions, final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
-            final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
+            final Collection<RateData> rateOptions, final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
             final List<EnumOptionData> valueConditionTypeOptions, final List<EnumOptionData> daysInMonthTypeOptions,
             final List<EnumOptionData> daysInYearTypeOptions, final List<EnumOptionData> interestRecalculationCompoundingTypeOptions,
             final List<EnumOptionData> rescheduleStrategyTypeOptions, final List<EnumOptionData> interestRecalculationFrequencyTypeOptions,
             final List<EnumOptionData> preCloseInterestCalculationStrategyOptions, final List<FloatingRateData> floatingRateOptions,
             final List<EnumOptionData> interestRecalculationNthDayTypeOptions,
-            final List<EnumOptionData> interestRecalculationDayOfWeekTypeOptions) {
+            final List<EnumOptionData> interestRecalculationDayOfWeekTypeOptions, final boolean isRatesEnabled) {
         this.id = productData.id;
         this.name = productData.name;
         this.shortName = productData.shortName;
@@ -779,6 +801,7 @@ public class LoanProductData {
         this.currency = productData.currency;
         this.fundOptions = fundOptions;
         this.transactionProcessingStrategyOptions = transactionStrategyOptions;
+        this.rateOptions = rateOptions;
         this.floatingRateOptions = floatingRateOptions;
         if (this.transactionProcessingStrategyOptions != null && this.transactionProcessingStrategyOptions.size() == 1) {
             final List<TransactionProcessingStrategyData> listOfOptions = new ArrayList<>(this.transactionProcessingStrategyOptions);
@@ -842,6 +865,8 @@ public class LoanProductData {
         this.syncExpectedWithDisbursementDate = productData.syncExpectedWithDisbursementDate;
         this.canUseForTopup = productData.canUseForTopup;
         this.isEqualAmortization = productData.isEqualAmortization;
+        this.rates = productData.rates;
+        this.isRatesEnabled = isRatesEnabled;
     }
 
     private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
index 723166c..0a27c1c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
@@ -60,6 +60,7 @@ import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
 import org.apache.fineract.portfolio.fund.domain.Fund;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
 import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
+import org.apache.fineract.portfolio.rate.domain.Rate;
 import org.joda.time.LocalDate;
 import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
 
@@ -102,6 +103,10 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
     @JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
     private List<Charge> charges;
 
+    @ManyToMany(fetch = FetchType.LAZY)
+    @JoinTable(name = "m_product_loan_rate", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "rate_id"))
+    private List<Rate> rates;
+
     @Embedded
     private LoanProductRelatedDetail loanProductRelatedDetail;
 
@@ -187,7 +192,8 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
     private boolean isEqualAmortization = false;
 
     public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
-            final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate) {
+            final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate,
+            final List<Rate> productRates) {
 
         final String name = command.stringValueOfParameterNamed("name");
         final String shortName = command.stringValueOfParameterNamed(LoanProductConstants.shortName);
@@ -353,7 +359,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
                 installmentAmountInMultiplesOf, loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate,
                 interestRateDifferential, minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate,
                 isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGapBetweenInstallments,
-                maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
+                maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, productRates);
 
     }
 
@@ -583,7 +589,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
             BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
             Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed,
             final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
-            final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization) {
+            final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization, final List<Rate> rates) {
         this.fund = fund;
         this.transactionProcessingStrategy = transactionProcessingStrategy;
         this.name = name.trim();
@@ -661,6 +667,10 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
         		syncExpectedWithDisbursementDate;
         this.canUseForTopup = canUseForTopup;
         this.isEqualAmortization = isEqualAmortization;
+
+        if(rates != null){
+            this.rates = rates;
+        }
     }
 
     public MonetaryCurrency getCurrency() {
@@ -702,6 +712,25 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
         return updated;
     }
 
+    public boolean updateRates(final List<Rate> newProductRates) {
+        if (newProductRates == null) { return false; }
+
+        boolean updated = false;
+        if (this.rates != null) {
+            final Set<Rate> currentSetOfCharges = new HashSet<>(this.rates);
+            final Set<Rate> newSetOfCharges = new HashSet<>(newProductRates);
+
+            if (!currentSetOfCharges.equals(newSetOfCharges)) {
+                updated = true;
+                this.rates = newProductRates;
+            }
+        } else {
+            updated = true;
+            this.rates = newProductRates;
+        }
+        return updated;
+    }
+
     public Integer getAccountingType() {
         return this.accountingRule;
     }
@@ -1046,6 +1075,13 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
             this.canUseForTopup = newValue;
         }
 
+        if (command.hasParameter(LoanProductConstants.ratesParamName)) {
+            final JsonArray jsonArray = command.arrayOfParameterNamed(LoanProductConstants.ratesParamName);
+            if (jsonArray != null) {
+                actualChanges.put(LoanProductConstants.ratesParamName, command.jsonFragment(LoanProductConstants.ratesParamName));
+            }
+        }
+
         return actualChanges;
     }
 
@@ -1382,4 +1418,13 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
         this.isEqualAmortization = isEqualAmortization;
     }
 
+
+    public List<Rate> getRates() {
+        return rates;
+    }
+
+    public void setRates(List<Rate> rates) {
+        this.rates = rates;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinMaxConstraints.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinMaxConstraints.java
index bc8dbe2..f076a6f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinMaxConstraints.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductMinMaxConstraints.java
@@ -165,4 +165,12 @@ public class LoanProductMinMaxConstraints {
 		this.maxNominalInterestRatePerPeriod = null;
 	}
 
+
+    public void setMinNominalInterestRatePerPeriod(BigDecimal minNominalInterestRatePerPeriod) {
+        this.minNominalInterestRatePerPeriod = minNominalInterestRatePerPeriod;
+    }
+
+    public void setMaxNominalInterestRatePerPeriod(BigDecimal maxNominalInterestRatePerPeriod) {
+        this.maxNominalInterestRatePerPeriod = maxNominalInterestRatePerPeriod;
+    }
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
index 76b57fb..000424a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
@@ -668,4 +668,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
         this.isEqualAmortization = isEqualAmortization;
     }
 
+    public void setNominalInterestRatePerPeriod(BigDecimal nominalInterestRatePerPeriod) {
+        this.nominalInterestRatePerPeriod = nominalInterestRatePerPeriod;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index 56446d0..06bbb68 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -108,7 +108,8 @@ public final class LoanProductDataValidator {
             LoanProductConstants.recalculationRestFrequencyWeekdayParamName,
             LoanProductConstants.recalculationRestFrequencyNthDayParamName, LoanProductConstants.recalculationRestFrequencyOnDayParamName,
             LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName,
-            LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam));
+            LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam,
+            LoanProductConstants.ratesParamName));
 
     private static final String[] supportedloanConfigurableAttributes = {LoanProductConstants.amortizationTypeParamName,
             LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyIdParamName,
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
index 00808fa..b5a08c1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
@@ -43,6 +43,8 @@ import org.apache.fineract.portfolio.loanproduct.data.LoanProductInterestRecalcu
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductParamType;
 import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
+import org.apache.fineract.portfolio.rate.data.RateData;
+import org.apache.fineract.portfolio.rate.service.RateReadService;
 import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.EmptyResultDataAccessException;
@@ -56,16 +58,18 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
     private final PlatformSecurityContext context;
     private final JdbcTemplate jdbcTemplate;
     private final ChargeReadPlatformService chargeReadPlatformService;
+    private final RateReadService rateReadService;
     private final FineractEntityAccessUtil fineractEntityAccessUtil;
 
     @Autowired
     public LoanProductReadPlatformServiceImpl(final PlatformSecurityContext context,
             final ChargeReadPlatformService chargeReadPlatformService, final RoutingDataSource dataSource,
-            final FineractEntityAccessUtil fineractEntityAccessUtil) {
+            final FineractEntityAccessUtil fineractEntityAccessUtil, final RateReadService rateReadService) {
         this.context = context;
         this.chargeReadPlatformService = chargeReadPlatformService;
         this.jdbcTemplate = new JdbcTemplate(dataSource);
         this.fineractEntityAccessUtil = fineractEntityAccessUtil;
+        this.rateReadService=rateReadService;
     }
 
     @Override
@@ -73,8 +77,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
 
         try {
             final Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanProductId);
+            final Collection<RateData> rates = this.rateReadService.retrieveProductLoanRates(loanProductId);
             final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas = retrieveLoanProductBorrowerCycleVariations(loanProductId);
-            final LoanProductMapper rm = new LoanProductMapper(charges, borrowerCycleVariationDatas);
+            final LoanProductMapper rm = new LoanProductMapper(charges, borrowerCycleVariationDatas, rates);
             final String sql = "select " + rm.loanProductSchema() + " where lp.id = ?";
 
             return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanProductId });
@@ -96,7 +101,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
 
         this.context.authenticatedUser();
 
-        final LoanProductMapper rm = new LoanProductMapper(null, null);
+        final LoanProductMapper rm = new LoanProductMapper(null, null, null);
 
         String sql = "select " + rm.loanProductSchema();
 
@@ -173,10 +178,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
 
         private final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas;
 
+        private final Collection<RateData> rates;
+
         public LoanProductMapper(final Collection<ChargeData> charges,
-                final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas) {
+                final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas, final Collection<RateData> rates) {
             this.charges = charges;
             this.borrowerCycleVariationDatas = borrowerCycleVariationDatas;
+            this.rates = rates;
         }
 
         public String loanProductSchema() {
@@ -449,6 +457,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
             final boolean syncExpectedWithDisbursementDate = rs.getBoolean("syncExpectedWithDisbursementDate");
             
             final boolean canUseForTopup = rs.getBoolean("canUseForTopup");
+            final Collection<RateData> rateOptions= null;
+            final boolean isRatesEnabled = false;
 
             return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
                     numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -465,7 +475,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
                     installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId,
                     floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
                     maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
-                    maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
+                    maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, this.rates, isRatesEnabled);
         }
     }
 
@@ -535,7 +545,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
     public Collection<LoanProductData> retrieveAllLoanProductsForCurrency(String currencyCode) {
         this.context.authenticatedUser();
 
-        final LoanProductMapper rm = new LoanProductMapper(null, null);
+        final LoanProductMapper rm = new LoanProductMapper(null, null, null);
 
         String sql = "select " + rm.loanProductSchema() + " where lp.currency_code= ? ";
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
index adfb9cd..6154a91 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
@@ -51,6 +51,7 @@ import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProces
 import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY;
 import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
+import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
@@ -59,6 +60,8 @@ import org.apache.fineract.portfolio.loanproduct.exception.LoanProductCannotBeMo
 import org.apache.fineract.portfolio.loanproduct.exception.LoanProductDateException;
 import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
 import org.apache.fineract.portfolio.loanproduct.serialization.LoanProductDataValidator;
+import org.apache.fineract.portfolio.rate.domain.Rate;
+import org.apache.fineract.portfolio.rate.domain.RateRepositoryWrapper;
 import org.joda.time.LocalDate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -81,6 +84,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
     private final FundRepository fundRepository;
     private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
     private final ChargeRepositoryWrapper chargeRepository;
+    private final RateRepositoryWrapper rateRepository;
     private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
     private final FineractEntityAccessUtil fineractEntityAccessUtil;
     private final FloatingRateRepositoryWrapper floatingRateRepository;
@@ -92,7 +96,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
             final LoanProductDataValidator fromApiJsonDeserializer, final LoanProductRepository loanProductRepository,
             final AprCalculator aprCalculator, final FundRepository fundRepository,
             final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
-            final ChargeRepositoryWrapper chargeRepository,
+            final ChargeRepositoryWrapper chargeRepository, final RateRepositoryWrapper rateRepository,
             final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
             final FineractEntityAccessUtil fineractEntityAccessUtil,
             final FloatingRateRepositoryWrapper floatingRateRepository,
@@ -105,6 +109,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
         this.fundRepository = fundRepository;
         this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
         this.chargeRepository = chargeRepository;
+        this.rateRepository = rateRepository;
         this.accountMappingWritePlatformService = accountMappingWritePlatformService;
         this.fineractEntityAccessUtil = fineractEntityAccessUtil;
         this.floatingRateRepository = floatingRateRepository;
@@ -130,6 +135,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
 
             final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
             final List<Charge> charges = assembleListOfProductCharges(command, currencyCode);
+            final List<Rate> rates = assembleListOfProductRates(command);
 
             FloatingRate floatingRate = null;
             if(command.parameterExists("floatingRatesId")){
@@ -137,7 +143,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
             			.findOneWithNotFoundDetection(command.longValueOfParameterNamed("floatingRatesId"));
             }
             final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command,
-                    this.aprCalculator, floatingRate);
+                    this.aprCalculator, floatingRate, rates);
             loanproduct.updateLoanProductInRelatedClasses();
 
             this.loanProductRepository.save(loanproduct);
@@ -242,6 +248,14 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
                     .updateLoanProductToGLAccountMapping(product.getId(), command, accountingTypeChanged, product.getAccountingType());
             changes.putAll(accountingMappingChanges);
 
+            if (changes.containsKey(LoanProductConstants.ratesParamName)) {
+                final List<Rate> productRates = assembleListOfProductRates(command);
+                final boolean updated = product.updateRates(productRates);
+                if (!updated) {
+                    changes.remove(LoanProductConstants.ratesParamName);
+                }
+            }
+
             if (!changes.isEmpty()) {
                 this.loanProductRepository.saveAndFlush(product);
             }
@@ -304,6 +318,28 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
         return charges;
     }
 
+    private List<Rate> assembleListOfProductRates(final JsonCommand command) {
+
+        final List<Rate> rates = new ArrayList<>();
+
+        if (command.parameterExists("rates")) {
+            final JsonArray ratesArray = command.arrayOfParameterNamed("rates");
+            if (ratesArray != null) {
+                List<Long> idList = new ArrayList<>();
+                for (int i = 0; i < ratesArray.size(); i++) {
+
+                    final JsonObject jsonObject = ratesArray.get(i).getAsJsonObject();
+                    if (jsonObject.has("id")) {
+                        idList.add(jsonObject.get("id").getAsLong());
+                    }
+                }
+                rates.addAll(this.rateRepository.findMultipleWithNotFoundDetection(idList));
+            }
+        }
+
+        return rates;
+    }
+
     /*
      * Guaranteed to throw an exception no matter what the data integrity issue
      * is.
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/api/RateApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/api/RateApiConstants.java
new file mode 100644
index 0000000..20d64d6
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/api/RateApiConstants.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.api;
+
+public class RateApiConstants {
+
+  public static final String approveUserIdParamName = "approveUserId";
+  public static final String rate = "rate";
+  public static final String rateName = "name";
+  public static final String ratePercentage = "percentage";
+  public static final String rateProductApply = "productApply";
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/api/RateApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/api/RateApiResource.java
new file mode 100644
index 0000000..8015831
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/api/RateApiResource.java
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.portfolio.rate.api;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.rate.data.RateData;
+import org.apache.fineract.portfolio.rate.service.RateReadService;
+import org.apache.fineract.portfolio.rate.service.RateWriteService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.Collection;
+
+/**
+ * Bowpi GT Created by Jose on 19/07/2017.
+ */
+
+@Path("/rates")
+@Component
+@Scope("singleton")
+public class RateApiResource {
+
+  private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
+      Arrays.asList("id", "name", "percentage", "productApply", "active"));
+  private final String resourceNameForPermissions = "RATE";
+  private final PlatformSecurityContext context;
+  private final RateReadService readPlatformService;
+  private final DefaultToApiJsonSerializer<RateData> toApiJsonSerializer;
+  private final ApiRequestParameterHelper apiRequestParameterHelper;
+  private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
+
+  @Autowired
+  public RateApiResource(final PlatformSecurityContext context,
+      final RateReadService rateReadService,
+      final DefaultToApiJsonSerializer<RateData> toApiJsonSerializer,
+      final ApiRequestParameterHelper apiRequestParameterHelper,
+      final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
+    this.context = context;
+    this.readPlatformService = rateReadService;
+    this.toApiJsonSerializer = toApiJsonSerializer;
+    this.apiRequestParameterHelper = apiRequestParameterHelper;
+    this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
+  }
+
+  @GET
+  @Path("{rateId}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public String retrieveRate(@PathParam("rateId") Long rateId, @Context final UriInfo uriInfo) {
+
+    this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+
+    final RateData rate = this.readPlatformService.retrieveOne(rateId);
+
+    final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
+        .process(uriInfo.getQueryParameters());
+
+    return this.toApiJsonSerializer.serialize(settings, rate, this.RESPONSE_DATA_PARAMETERS);
+  }
+
+  @POST
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public String createRate(final String apiRequestBodyAsJson) {
+    final CommandWrapper commandRequest = new CommandWrapperBuilder().createRate()
+        .withJson(apiRequestBodyAsJson).build();
+
+    final CommandProcessingResult result = this.commandsSourceWritePlatformService
+        .logCommandSource(commandRequest);
+
+    return this.toApiJsonSerializer.serialize(result);
+
+  }
+
+  @GET
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public String getAllRates(@Context final UriInfo uriInfo) {
+
+    this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+
+    Collection<RateData> rates = this.readPlatformService.retrieveAllRates();
+
+    final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
+        .process(uriInfo.getQueryParameters());
+
+    return this.toApiJsonSerializer.serialize(settings, rates, this.RESPONSE_DATA_PARAMETERS);
+  }
+
+  @PUT
+  @Path("{rateId}")
+  @Consumes({MediaType.APPLICATION_JSON})
+  @Produces({MediaType.APPLICATION_JSON})
+  public String updateRate(@PathParam("rateId") Long rateId, final String apiRequestBodyAsJson) {
+    final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRate(rateId)
+        .withJson(apiRequestBodyAsJson).build();
+
+    final CommandProcessingResult result = this.commandsSourceWritePlatformService
+        .logCommandSource(commandRequest);
+
+    return this.toApiJsonSerializer.serialize(result);
+  }
+
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/data/RateData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/data/RateData.java
new file mode 100644
index 0000000..d60c8dd
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/data/RateData.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.portfolio.rate.data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * Bowpi GT Created by Jose on 19/07/2017.
+ */
+public class RateData implements Serializable {
+
+  private Long id;
+
+  private String name;
+
+  private BigDecimal percentage;
+
+  private String productApply;
+
+  private boolean active;
+
+  public static RateData instance(final Long id, final String name, final BigDecimal percentage,
+      final String productApply, final boolean active) {
+    return new RateData(id, name, percentage, productApply, active);
+  }
+
+  private RateData(final Long id, final String name, final BigDecimal percentage,
+      final String productApply, final boolean active) {
+    this.id = id;
+    this.name = name;
+    this.percentage = percentage;
+    this.productApply = productApply;
+    this.active = active;
+  }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/Rate.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/Rate.java
new file mode 100644
index 0000000..3d0803b
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/Rate.java
@@ -0,0 +1,201 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.portfolio.rate.domain;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.useradministration.domain.AppUser;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+/**
+ * Bowpi GT Created by Jose on 19/07/2017.
+ */
+
+@Entity
+@Table(name = "m_rate", uniqueConstraints = {
+    @UniqueConstraint(columnNames = {"name"}, name = "name")})
+public class Rate extends AbstractAuditableCustom<AppUser, Long> {
+
+  @Column(name = "name", length = 250, unique = true)
+  private String name;
+
+  @Column(name = "percentage", scale = 10, precision = 2, nullable = false)
+  private BigDecimal percentage;
+
+  @Column(name = "product_apply", length = 100)
+  private String productApply;
+
+  @Column(name = "active", nullable = false)
+  private boolean active;
+
+  @ManyToOne
+  @JoinColumn(name = "approve_user", nullable = true)
+  private AppUser approveUser;
+
+
+  public Rate() {
+  }
+
+
+  public Rate(String name, BigDecimal percentage, String productApply, boolean active,
+      AppUser approveUser) {
+    this.name = name;
+    this.percentage = percentage;
+    this.productApply = productApply;
+    this.active = active;
+    this.approveUser = approveUser;
+  }
+
+  public Rate(String name, BigDecimal percentage, String productApply, boolean active) {
+    this.name = name;
+    this.percentage = percentage;
+    this.productApply = productApply;
+    this.active = active;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public BigDecimal getPercentage() {
+    return percentage;
+  }
+
+  public void setPercentage(BigDecimal percentage) {
+    this.percentage = percentage;
+  }
+
+  public boolean isActive() {
+    return active;
+  }
+
+  public void setActive(boolean active) {
+    this.active = active;
+  }
+
+  public AppUser getApproveUser() {
+    return approveUser;
+  }
+
+  public void setApproveUser(AppUser approveUser) {
+    this.approveUser = approveUser;
+  }
+
+  public String getProductApply() {
+    return productApply;
+  }
+
+  public void setProductApply(String productApply) {
+    this.productApply = productApply;
+  }
+
+  @Override
+  public String toString() {
+    return "Rate{" +
+        "name='" + name + '\'' +
+        ", percentage=" + percentage +
+        ", productApply='" + productApply + '\'' +
+        ", active=" + active +
+        ", approveUser=" + approveUser +
+        '}';
+  }
+
+  public static Rate from(String name, BigDecimal percentage, String productApply, Boolean active) {
+    return new Rate(name, percentage, productApply, active);
+  }
+
+  public static Rate fromJson(final JsonCommand command, AppUser user) {
+
+    final String name = command.stringValueOfParameterNamed("name");
+
+    final BigDecimal percentage = command.bigDecimalValueOfParameterNamed("percentage");
+
+    final String productApply = command.stringValueOfParameterNamed("productApply");
+
+    final boolean active = command.booleanPrimitiveValueOfParameterNamed("active");
+
+    return new Rate(name, percentage, productApply, active, user);
+  }
+
+  public Map<String, Object> update(final JsonCommand command) {
+
+    final Map<String, Object> actualChanges = new LinkedHashMap<>(7);
+
+    final String nameParamName = "name";
+    if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
+      final String newValue = command.stringValueOfParameterNamed(nameParamName);
+      actualChanges.put(nameParamName, newValue);
+      this.name = StringUtils.defaultIfEmpty(newValue, null);
+    }
+
+    final String percentageParamName = "percentage";
+    if (command.isChangeInBigDecimalParameterNamed(percentageParamName, this.percentage)) {
+      final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(percentageParamName);
+      actualChanges.put(percentageParamName, newValue);
+      this.percentage = newValue;
+    }
+
+    final String productApplyParamName = "productApply";
+    if (command.isChangeInStringParameterNamed(productApplyParamName, this.productApply)) {
+      final String newValue = command.stringValueOfParameterNamed(productApplyParamName);
+      actualChanges.put(productApplyParamName, newValue);
+      this.productApply = StringUtils.defaultIfEmpty(newValue, null);
+    }
+
+    final String activeParamName = "active";
+    if (command.isChangeInBooleanParameterNamed(activeParamName, this.active)) {
+      final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(activeParamName);
+      actualChanges.put(activeParamName, newValue);
+      this.active = newValue;
+    }
+
+    final String approveUserParamName = "approveUserId";
+    if (command.isChangeInLongParameterNamed(approveUserParamName, getApproveUserId())) {
+      final Long newValue = command.longValueOfParameterNamed(approveUserParamName);
+      actualChanges.put(approveUserParamName, newValue);
+    }
+
+    return actualChanges;
+  }
+
+  private Long getApproveUserId() {
+    Long approveUserId = null;
+    if (this.approveUser != null) {
+      approveUserId = this.approveUser.getId();
+    }
+    return approveUserId;
+  }
+
+  public void assembleFrom(String name, BigDecimal percentage, String productApply, boolean active){
+    this.name = name;
+    this.percentage = percentage;
+    this.productApply = productApply;
+    this.active = active;
+  }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateRepository.java
new file mode 100644
index 0000000..e593ac8
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateRepository.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.domain;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+/**
+ * Bowpi GT
+ * Rate repository to save on m_rate table (custom change for Credi Chapin)
+ * Created by Jose on 19/07/2017.
+ */
+public interface RateRepository extends JpaRepository<Rate, Long>, JpaSpecificationExecutor<Rate> {
+
+    Rate findByName(String name);
+    List<Rate> findAllByActiveAndProductApply(boolean active, String productApply);
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateRepositoryWrapper.java
new file mode 100644
index 0000000..7604a11
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/domain/RateRepositoryWrapper.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.fineract.portfolio.rate.exception.RateNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class RateRepositoryWrapper {
+
+  private final RateRepository repository;
+
+  @Autowired
+  public RateRepositoryWrapper(final RateRepository repository) {
+    this.repository = repository;
+  }
+
+  public Rate findOneWithNotFoundDetection(final Long rateId) {
+
+    final Rate rate = this.repository.findOne(rateId);
+    if (rate == null) {
+      throw new RateNotFoundException(rateId);
+    }
+
+    return rate;
+  }
+
+  public List<Rate> findMultipleWithNotFoundDetection(final List<Long> rateIds) {
+    List<Rate> rates = new ArrayList<>();
+    if (rateIds != null && !rateIds.isEmpty()) {
+      final List<Rate> foundRates = this.repository.findAll(rateIds);
+      for (Long rateId : rateIds) {
+        Boolean found = false;
+        for (Rate foundRate : foundRates) {
+          if (Objects.equals(foundRate.getId(),
+              rateId)) {
+            found = true;
+            break;
+          }
+        }
+        if (!found) {
+          throw new RateNotFoundException(rateId);
+        }
+      }
+      rates.addAll(foundRates);
+    }
+    return rates;
+  }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/exception/RateAlreadyExistException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/exception/RateAlreadyExistException.java
new file mode 100644
index 0000000..f0a844e
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/exception/RateAlreadyExistException.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.exception;
+
+/**
+ * Bowpi GT
+ * Created by Jose on 24/07/2017.
+ */
+public class RateAlreadyExistException extends Exception{
+
+    public RateAlreadyExistException(String s) {
+        super(s);
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/exception/RateNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/exception/RateNotFoundException.java
new file mode 100644
index 0000000..48cf904
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/exception/RateNotFoundException.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.portfolio.rate.exception;
+
+import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
+
+public class RateNotFoundException extends AbstractPlatformResourceNotFoundException {
+
+
+  public RateNotFoundException(final Long id) {
+    super("error.msg.rate.id.invalid", "Rate with identifier " + id + " does not exist", id);
+  }
+
+  public RateNotFoundException(final String name) {
+    super("error.msg.rate.id.invalid", "Rate with name " + name + " does not exist", name);
+  }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/handler/CreateRateCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/handler/CreateRateCommandHandler.java
new file mode 100644
index 0000000..8794540
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/handler/CreateRateCommandHandler.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.rate.service.RateWriteService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Bowpi GT
+ * Created by Jose on 19/07/2017.
+ */
+@Service
+@CommandType(entity = "RATE", action = "CREATE")
+public class CreateRateCommandHandler implements NewCommandSourceHandler {
+
+  private final RateWriteService writePlatformService;
+
+  @Autowired
+  public CreateRateCommandHandler(final RateWriteService writePlatformService) {
+    this.writePlatformService = writePlatformService;
+  }
+
+  @Override
+  public CommandProcessingResult processCommand(final JsonCommand command) {
+    return this.writePlatformService.createRate(command);
+  }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/handler/UpdateRateCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/handler/UpdateRateCommandHandler.java
new file mode 100644
index 0000000..cc22e53
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/handler/UpdateRateCommandHandler.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.rate.service.RateWriteService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Bowpi GT
+ * Created by Jose on 19/07/2017.
+ */
+@Service
+@CommandType(entity = "RATE", action = "UPDATE")
+public class UpdateRateCommandHandler implements NewCommandSourceHandler {
+
+  private final RateWriteService writePlatformService;
+
+  @Autowired
+  public UpdateRateCommandHandler(final RateWriteService writePlatformService) {
+    this.writePlatformService = writePlatformService;
+  }
+
+  @Transactional
+  @Override
+  public CommandProcessingResult processCommand(final JsonCommand command) {
+
+    return this.writePlatformService.updateRate(command.entityId(), command);
+  }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/serialization/RateDefinitionCommandFromApiJsonDeserializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/serialization/RateDefinitionCommandFromApiJsonDeserializer.java
new file mode 100644
index 0000000..acfbc72
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/serialization/RateDefinitionCommandFromApiJsonDeserializer.java
@@ -0,0 +1,129 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.serialization;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.portfolio.rate.api.RateApiConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RateDefinitionCommandFromApiJsonDeserializer {
+
+  /**
+   * The parameters supported for this command.
+   */
+  private final Set<String> supportedParameters = new HashSet<>(
+      Arrays.asList("id", "name", "percentage", "productApply", "active", "approveUser", "locale"));
+
+  private final FromJsonHelper fromApiJsonHelper;
+
+  @Autowired
+  public RateDefinitionCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
+    this.fromApiJsonHelper = fromApiJsonHelper;
+  }
+
+  public void validateForCreate(final String json) {
+    if (StringUtils.isBlank(json)) {
+      throw new InvalidJsonException();
+    }
+
+    final Type typeOfMap = new TypeToken<Map<String, Object>>() {
+    }.getType();
+
+    this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
+
+    final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+
+    final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
+        .resource(RateApiConstants.rateName);
+
+    final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+    final String name = this.fromApiJsonHelper.extractStringNamed(RateApiConstants.rateName, element);
+    baseDataValidator.reset().parameter(RateApiConstants.rateName).value(name).notBlank().notExceedingLengthOf(250);
+
+    final BigDecimal percentage = this.fromApiJsonHelper
+        .extractBigDecimalWithLocaleNamed(RateApiConstants.ratePercentage, element);
+    baseDataValidator.reset().parameter(RateApiConstants.ratePercentage).value(percentage).notBlank();
+
+    final String productApply = this.fromApiJsonHelper.extractStringNamed(RateApiConstants.rateProductApply, element);
+    baseDataValidator.reset().parameter(RateApiConstants.rateProductApply).value(productApply).notBlank()
+        .notExceedingLengthOf(100);
+
+    throwExceptionIfValidationWarningsExist(dataValidationErrors);
+  }
+
+  public void validateForUpdate(final String json) {
+    if (StringUtils.isBlank(json)) {
+      throw new InvalidJsonException();
+    }
+
+    final Type typeOfMap = new TypeToken<Map<String, Object>>() {
+    }.getType();
+    this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
+
+    final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+    final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
+        .resource(RateApiConstants.rate);
+
+    final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+    if (this.fromApiJsonHelper.parameterExists(RateApiConstants.rateName, element)) {
+      final String name = this.fromApiJsonHelper.extractStringNamed(RateApiConstants.rateName, element);
+      baseDataValidator.reset().parameter(RateApiConstants.rateName).value(name).notBlank().notExceedingLengthOf(250);
+    }
+
+    if (this.fromApiJsonHelper.parameterExists(RateApiConstants.ratePercentage, element)) {
+      final BigDecimal percentage = this.fromApiJsonHelper
+          .extractBigDecimalWithLocaleNamed(RateApiConstants.ratePercentage, element);
+      baseDataValidator.reset().parameter(RateApiConstants.ratePercentage).value(percentage).notBlank();
+    }
+
+    if (this.fromApiJsonHelper.parameterExists(RateApiConstants.rateProductApply, element)) {
+      final String productApply = this.fromApiJsonHelper.extractStringNamed(RateApiConstants.rateProductApply, element);
+      baseDataValidator.reset().parameter(RateApiConstants.rateProductApply).value(productApply).notBlank();
+    }
+
+    throwExceptionIfValidationWarningsExist(dataValidationErrors);
+  }
+
+  private void throwExceptionIfValidationWarningsExist(
+      final List<ApiParameterError> dataValidationErrors) {
+    if (!dataValidationErrors.isEmpty()) {
+      throw new PlatformApiDataValidationException(dataValidationErrors);
+    }
+  }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateAssembler.java
new file mode 100644
index 0000000..9b2d6f9
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateAssembler.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.service;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
+import org.apache.fineract.portfolio.rate.domain.Rate;
+import org.apache.fineract.portfolio.rate.domain.RateRepositoryWrapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class RateAssembler {
+
+  private final FromJsonHelper fromApiJsonHelper;
+  private final RateRepositoryWrapper rateRepository;
+
+  @Autowired
+  public RateAssembler(final FromJsonHelper fromApiJsonHelper,
+      final RateRepositoryWrapper rateRepository) {
+    this.fromApiJsonHelper = fromApiJsonHelper;
+    this.rateRepository = rateRepository;
+  }
+
+  public List<Rate> fromParsedJson(final JsonElement element) {
+
+    final List<Rate> rateItems = new ArrayList<>();
+
+    if (element.isJsonObject()) {
+      final JsonObject topLevelJsonElement = element.getAsJsonObject();
+      final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
+
+      if (topLevelJsonElement.has(LoanProductConstants.ratesParamName) && topLevelJsonElement
+          .get(LoanProductConstants.ratesParamName)
+          .isJsonArray()) {
+        final JsonArray array = topLevelJsonElement.get(LoanProductConstants.ratesParamName)
+            .getAsJsonArray();
+        List<Long> idList = new ArrayList<>();
+
+        for (int i = 0; i < array.size(); i++) {
+
+          final JsonObject rateElement = array.get(i).getAsJsonObject();
+
+          final Long id = this.fromApiJsonHelper.extractLongNamed("id", rateElement);
+
+          if (id != null) {
+            final Long rateId = this.fromApiJsonHelper.extractLongNamed("id", rateElement);
+            idList.add(rateId);
+          }
+        }
+        rateItems.addAll(rateRepository.findMultipleWithNotFoundDetection(idList));
+      }
+    }
+
+    return rateItems;
+  }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadService.java
new file mode 100644
index 0000000..7b4b051
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadService.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.service;
+
+import org.apache.fineract.portfolio.rate.data.RateData;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Bowpi GT
+ * Created by Jose on 19/07/2017.
+ */
+public interface RateReadService {
+
+  Collection<RateData> retrieveAllRates();
+
+  Collection<RateData> retrieveLoanApplicableRates();
+
+  RateData retrieveOne(Long rateId);
+
+  RateData retrieveByName(String name);
+
+  List<RateData> retrieveProductLoanRates(Long loanId);
+
+  List<RateData> retrieveLoanRates(Long loanId);
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadServiceImpl.java
new file mode 100644
index 0000000..44682de
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateReadServiceImpl.java
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.portfolio.rate.service;
+
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.rate.exception.RateNotFoundException;
+import org.apache.fineract.portfolio.rate.domain.Rate;
+import org.apache.fineract.portfolio.rate.data.RateData;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Bowpi GT Created by Jose on 19/07/2017.
+ */
+@Service
+public class RateReadServiceImpl implements RateReadService {
+
+  private final JdbcTemplate jdbcTemplate;
+  private final PlatformSecurityContext context;
+
+  @Autowired
+  public RateReadServiceImpl(PlatformSecurityContext context, final RoutingDataSource dataSource) {
+    this.context = context;
+    this.jdbcTemplate = new JdbcTemplate(dataSource);
+  }
+
+  @Override
+  public Collection<RateData> retrieveAllRates() {
+    this.context.authenticatedUser();
+    final RateMapper rm = new RateMapper();
+    final String sql = "select " + rm.rateSchema();
+    return this.jdbcTemplate.query(sql, rm, new Object[]{});
+  }
+
+  @Override
+  public RateData retrieveOne(Long rateId) {
+    try {
+      this.context.authenticatedUser();
+      final RateMapper rm = new RateMapper();
+      final String sql = "select " + rm.rateSchema() + " where r.id = ?";
+      final RateData selectedRate = this.jdbcTemplate.queryForObject(sql, rm, new Object[]{rateId});
+      return selectedRate;
+
+    } catch (final EmptyResultDataAccessException e) {
+      throw new RateNotFoundException(rateId);
+    }
+  }
+
+  @Override
+  public RateData retrieveByName(String name) {
+    try {
+      this.context.authenticatedUser();
+      final RateMapper rm = new RateMapper();
+      final String sql = "select " + rm.rateSchema() + " where r.name = ?";
+      final RateData selectedRate = this.jdbcTemplate.queryForObject(sql, rm, new Object[]{name});
+      return selectedRate;
+
+    } catch (final EmptyResultDataAccessException e) {
+      throw new RateNotFoundException(name);
+    }
+  }
+
+  @Override
+  public Collection<RateData> retrieveLoanApplicableRates() {
+    this.context.authenticatedUser();
+    final RateMapper rm = new RateMapper();
+    final String sql = "select " + rm.rateSchema() + " where r.active = ? and product_apply=?";
+    return this.jdbcTemplate.query(sql, rm, new Object[]{true, "m_loan"});
+  }
+
+  @Override
+  public List<RateData> retrieveLoanRates(Long loanId) {
+    final RateMapper rm = new RateMapper();
+    final String sql = "select " + rm.loanRateSchema() + " where lr.loan_id = ?";
+    return this.jdbcTemplate.query(sql, rm, new Object[]{loanId});
+  }
+
+  @Override
+  public List<RateData> retrieveProductLoanRates(Long loanId) {
+    final RateMapper rm = new RateMapper();
+    final String sql = "select " + rm.productLoanRateSchema() + " where lr.product_loan_id = ?";
+    return this.jdbcTemplate.query(sql, rm, new Object[]{loanId});
+  }
+
+  private static final class RateMapper implements RowMapper<RateData> {
+
+
+    public String rateSchema() {
+      return " r.id as id, r.name as name, r.percentage as percentage, " +
+          "r.product_apply as productApply, r.active as active from m_rate r ";
+    }
+
+    public String loanRateSchema() {
+      return rateSchema() + " join m_loan_rate lr on lr.rate_id = r.id";
+    }
+
+    public String productLoanRateSchema() {
+      return rateSchema() + " join m_product_loan_rate lr on lr.rate_id = r.id";
+    }
+
+    public RateMapper() {
+    }
+
+    @Override
+    public RateData mapRow(ResultSet resultSet, int i) throws SQLException {
+      final Long id = resultSet.getLong("id");
+      final String name = resultSet.getString("name");
+      final BigDecimal percentage = resultSet.getBigDecimal("percentage");
+      final String productApply = resultSet.getString("productApply");
+      final boolean active = resultSet.getBoolean("active");
+      return RateData.instance(id, name, percentage, productApply, active);
+    }
+
+    public RateData mapRow(Rate rateResponse, int i) {
+      final Long id = rateResponse.getId();
+      final String name = rateResponse.getName();
+      final BigDecimal percentage = rateResponse.getPercentage();
+      final String productApply = rateResponse.getProductApply();
+      final boolean active = rateResponse.isActive();
+      return RateData.instance(id, name, percentage, productApply, active);
+    }
+  }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateWriteService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateWriteService.java
new file mode 100644
index 0000000..0cd7dfe
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateWriteService.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.service;
+
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+
+/**
+ * Bowpi GT
+ * Created by Jose on 19/07/2017.
+ */
+public interface RateWriteService {
+
+    CommandProcessingResult createRate(final JsonCommand command);
+
+    CommandProcessingResult updateRate(Long rateId, JsonCommand command);
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateWriteServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateWriteServiceImpl.java
new file mode 100644
index 0000000..51f97b4
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/rate/service/RateWriteServiceImpl.java
@@ -0,0 +1,157 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.rate.service;
+
+import static org.apache.fineract.portfolio.rate.api.RateApiConstants.approveUserIdParamName;
+
+import java.util.Map;
+import javax.persistence.PersistenceException;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.rate.domain.Rate;
+import org.apache.fineract.portfolio.rate.domain.RateRepository;
+import org.apache.fineract.portfolio.rate.exception.RateNotFoundException;
+import org.apache.fineract.portfolio.rate.serialization.RateDefinitionCommandFromApiJsonDeserializer;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.apache.fineract.useradministration.domain.AppUserRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Bowpi GT Created by Jose on 19/07/2017.
+ */
+@Service
+public class RateWriteServiceImpl implements RateWriteService {
+
+  private final static Logger logger = LoggerFactory
+      .getLogger(RateWriteServiceImpl.class);
+  private final RateRepository rateRepository;
+  private final AppUserRepository appUserRepository;
+  private final PlatformSecurityContext context;
+  private final RateDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer;
+
+  @Autowired
+  public RateWriteServiceImpl(RateRepository rateRepository, AppUserRepository appUserRepository,
+      final RateDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer,
+      PlatformSecurityContext context) {
+    this.rateRepository = rateRepository;
+    this.appUserRepository = appUserRepository;
+    this.context = context;
+    this.fromApiJsonDeserializer = fromApiJsonDeserializer;
+  }
+
+  @Override
+  public CommandProcessingResult createRate(JsonCommand command) {
+    try {
+      this.context.authenticatedUser();
+      this.fromApiJsonDeserializer.validateForCreate(command.json());
+
+      final Long approveUserId = command.longValueOfParameterNamed(approveUserIdParamName);
+      AppUser approveUser = null;
+      if (approveUserId != null) {
+        approveUser = this.appUserRepository.findOne(approveUserId);
+      }
+      final Rate rate = Rate.fromJson(command, approveUser);
+
+      this.rateRepository.save(rate);
+
+      return new CommandProcessingResultBuilder().withCommandId(command.commandId())
+          .withEntityId(rate.getId()).build();
+
+    } catch (final DataIntegrityViolationException dve) {
+      handleRateDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
+      return CommandProcessingResult.empty();
+    } catch (final PersistenceException dve) {
+      Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
+      handleRateDataIntegrityIssues(command, throwable, dve);
+      return CommandProcessingResult.empty();
+    }
+  }
+
+  @Transactional
+  @Override
+  public CommandProcessingResult updateRate(final Long rateId, final JsonCommand command) {
+    try {
+      this.context.authenticatedUser();
+
+      final Rate rateToUpdate = this.rateRepository.findOne(rateId);
+      if (rateToUpdate == null) {
+        throw new RateNotFoundException(rateId);
+      }
+
+      final Map<String, Object> changes = rateToUpdate.update(command);
+
+      this.fromApiJsonDeserializer.validateForUpdate(command.json());
+
+      if (changes.containsKey(approveUserIdParamName)) {
+        final Long newValue = (Long) changes.get(approveUserIdParamName);
+        AppUser newApproveUser = null;
+        if (newValue != null) {
+          newApproveUser = this.appUserRepository.findOne(newValue);
+        }
+        rateToUpdate.setApproveUser(newApproveUser);
+      }
+      if (!changes.isEmpty()) {
+        this.rateRepository.saveAndFlush(rateToUpdate);
+      }
+
+      return new CommandProcessingResultBuilder() //
+          .withCommandId(command.commandId()) //
+          .withEntityId(rateId) //
+          .with(changes) //
+          .build();
+
+    } catch (final DataIntegrityViolationException dve) {
+      handleRateDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
+      return new CommandProcessingResult(Long.valueOf(-1));
+    } catch (final PersistenceException dve) {
+      Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
+      handleRateDataIntegrityIssues(command, throwable, dve);
+      return CommandProcessingResult.empty();
+    }
+
+  }
+
+   /*
+   * Guaranteed to throw an exception no matter what the data integrity issue
+   * is.
+   */
+  private void handleRateDataIntegrityIssues(final JsonCommand command, final Throwable realCause,
+      final Exception dve) {
+    if (realCause.getMessage().contains("rate_name_org")) {
+      final String name = command.stringValueOfParameterNamed("name");
+      throw new PlatformDataIntegrityException("error.msg.fund.duplicate.externalId",
+          "A rate with name '" + name
+              + "' already exists", "name", name);
+    }
+
+    logger.error(dve.getMessage(), dve);
+    throw new PlatformDataIntegrityException("error.msg.fund.unknown.data.integrity.issue",
+        "Unknown data integrity issue with resource: " + realCause.getMessage());
+  }
+
+}
diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V352__rates.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V352__rates.sql
new file mode 100644
index 0000000..1ca0986
--- /dev/null
+++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V352__rates.sql
@@ -0,0 +1,67 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+CREATE TABLE IF NOT EXISTS `m_rate` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `name` varchar(250) NOT NULL,
+  `percentage` decimal(10,2) NOT NULL,
+  `active` tinyint(1) DEFAULT '0',
+  `product_apply` varchar(100) NOT NULL,
+  `created_date` datetime NULL DEFAULT NULL,
+  `createdby_id` bigint(20) NOT NULL,
+  `lastmodifiedby_id` bigint(20) NOT NULL,
+  `lastmodified_date` datetime NULL DEFAULT NULL,
+  `approve_user` bigint(20) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `FK_M_RATE_CREATE_USER` (`createdby_id`),
+  KEY `FK_M_RATE_APPROVE_USER` (`approve_user`),
+  CONSTRAINT `FK_M_RATE_APPROVE_USER` FOREIGN KEY (`approve_user`) REFERENCES `m_appuser` (`id`),
+  CONSTRAINT `FK_M_RATE_CREATE_USER` FOREIGN KEY (`createdby_id`) REFERENCES `m_appuser` (`id`)
+);
+
+
+CREATE TABLE IF NOT EXISTS `m_loan_rate` (
+  `loan_id` bigint(20) NOT NULL,
+  `rate_id` bigint(20) NOT NULL,
+  PRIMARY KEY (`loan_id`,`rate_id`),
+  KEY `FK_M_LOAN_RATE_RATE` (`rate_id`),
+  CONSTRAINT `FK_M_LOAN_RATE_LOAN` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`),
+  CONSTRAINT `FK_M_LOAN_RATE_RATE` FOREIGN KEY (`rate_id`) REFERENCES `m_rate` (`id`)
+);
+
+
+CREATE TABLE IF NOT EXISTS `m_product_loan_rate` (
+  `product_loan_id` bigint(20) NOT NULL,
+  `rate_id` bigint(20) NOT NULL,
+  PRIMARY KEY (`product_loan_id`,`rate_id`),
+  KEY `FK_M_PRODUCT_LOAN_RATE_RATE` (`rate_id`),
+  CONSTRAINT `FK_M_PRODUCT_LOAN_RATE_LOAN` FOREIGN KEY (`product_loan_id`) REFERENCES `m_product_loan` (`id`),
+  CONSTRAINT `FK_M_PRODUCT_LOAN_RATE_RATE` FOREIGN KEY (`rate_id`) REFERENCES `m_rate` (`id`)
+);
+
+
+INSERT INTO `m_permission`
+(`grouping`,`code`,`entity_name`,`action_name`,`can_maker_checker`) VALUES
+  ('organisation', 'CREATE_RATE', 'RATE', 'CREATE', '1'),
+  ('organisation', 'UPDATE_RATE', 'RATE', 'UPDATE', '1');
+
+INSERT INTO `c_configuration`
+(`name`, `value`, `enabled`, `is_trap_door`, `description`) VALUES
+('vat-tax', 12, 0, 0, 'VAT tax'),
+('sub-rates', 12, 0, 0, 'Enable Rates Module');;