Merge pull request #2 from myrlen/develop

Checking for invalid customer.  Changing input of interest.
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/domain/Case.java b/api/src/main/java/io/mifos/portfolio/api/v1/domain/Case.java
index cebfaa2..542e7ca 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/domain/Case.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/domain/Case.java
@@ -15,10 +15,14 @@
  */
 package io.mifos.portfolio.api.v1.domain;
 
-import io.mifos.portfolio.api.v1.validation.ValidAccountAssignments;
 import io.mifos.core.lang.validation.constraints.ValidIdentifier;
+import io.mifos.portfolio.api.v1.validation.ValidAccountAssignments;
 import org.hibernate.validator.constraints.NotBlank;
 
+import javax.validation.constraints.DecimalMax;
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
 import java.util.Objects;
 import java.util.Set;
 
@@ -31,6 +35,12 @@
   private String identifier;
   @ValidIdentifier
   private String productIdentifier;
+
+  @DecimalMin(value = "0.00")
+  @DecimalMax(value = "999.99")
+  @NotNull
+  private BigDecimal interest;
+
   @NotBlank
   private String parameters;
   @ValidAccountAssignments
@@ -61,6 +71,14 @@
     this.productIdentifier = productIdentifier;
   }
 
+  public BigDecimal getInterest() {
+    return interest;
+  }
+
+  public void setInterest(BigDecimal interest) {
+    this.interest = interest;
+  }
+
   public String getParameters() {
     return parameters;
   }
@@ -123,30 +141,32 @@
     if (o == null || getClass() != o.getClass()) return false;
     Case aCase = (Case) o;
     return Objects.equals(identifier, aCase.identifier) &&
-            Objects.equals(productIdentifier, aCase.productIdentifier) &&
-            Objects.equals(parameters, aCase.parameters) &&
-            Objects.equals(accountAssignments, aCase.accountAssignments) &&
-            currentState == aCase.currentState;
+        Objects.equals(productIdentifier, aCase.productIdentifier) &&
+        Objects.equals(interest, aCase.interest) &&
+        Objects.equals(parameters, aCase.parameters) &&
+        Objects.equals(accountAssignments, aCase.accountAssignments) &&
+        currentState == aCase.currentState;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(identifier, productIdentifier, parameters, accountAssignments, currentState);
+    return Objects.hash(identifier, productIdentifier, interest, parameters, accountAssignments, currentState);
   }
 
   @Override
   public String toString() {
     return "Case{" +
-            "identifier='" + identifier + '\'' +
-            ", productIdentifier='" + productIdentifier + '\'' +
-            ", parameters='" + parameters + '\'' +
-            ", accountAssignments=" + accountAssignments +
-            ", currentState=" + currentState +
-            ", createdOn='" + createdOn + '\'' +
-            ", createdBy='" + createdBy + '\'' +
-            ", lastModifiedOn='" + lastModifiedOn + '\'' +
-            ", lastModifiedBy='" + lastModifiedBy + '\'' +
-            '}';
+        "identifier='" + identifier + '\'' +
+        ", productIdentifier='" + productIdentifier + '\'' +
+        ", interest=" + interest +
+        ", parameters='" + parameters + '\'' +
+        ", accountAssignments=" + accountAssignments +
+        ", currentState=" + currentState +
+        ", createdOn='" + createdOn + '\'' +
+        ", createdBy='" + createdBy + '\'' +
+        ", lastModifiedOn='" + lastModifiedOn + '\'' +
+        ", lastModifiedBy='" + lastModifiedBy + '\'' +
+        '}';
   }
 
   public enum State {
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/domain/ChargeDefinition.java b/api/src/main/java/io/mifos/portfolio/api/v1/domain/ChargeDefinition.java
index 2268871..b7170d0 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/domain/ChargeDefinition.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/domain/ChargeDefinition.java
@@ -39,7 +39,8 @@
   @SuppressWarnings("WeakerAccess")
   public enum ChargeMethod {
     FIXED,
-    PROPORTIONAL
+    PROPORTIONAL,
+    INTEREST
   }
 
   @ValidIdentifier
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/domain/InterestRange.java b/api/src/main/java/io/mifos/portfolio/api/v1/domain/InterestRange.java
new file mode 100644
index 0000000..d119bd7
--- /dev/null
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/domain/InterestRange.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.portfolio.api.v1.domain;
+
+import org.hibernate.validator.constraints.ScriptAssert;
+
+import javax.validation.constraints.DecimalMax;
+import javax.validation.constraints.DecimalMin;
+import java.math.BigDecimal;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+@ScriptAssert(lang = "javascript", script = "_this.maximum != null && _this.minimum != null && _this.maximum.compareTo(_this.minimum) >= 0 && _this.minimum.scale() <= 2 && _this.maximum.scale() <= 2")
+public class InterestRange {
+  @DecimalMin(value = "0.00")
+  @DecimalMax(value = "999.99")
+  private BigDecimal minimum;
+  @DecimalMin(value = "0.00")
+  @DecimalMax(value = "999.99")
+  private BigDecimal maximum;
+
+  public InterestRange() {
+  }
+
+  public InterestRange(BigDecimal minimum, BigDecimal maximum) {
+    this.minimum = minimum;
+    this.maximum = maximum;
+  }
+
+  public BigDecimal getMinimum() {
+    return minimum;
+  }
+
+  public void setMinimum(BigDecimal minimum) {
+    this.minimum = minimum;
+  }
+
+  public BigDecimal getMaximum() {
+    return maximum;
+  }
+
+  public void setMaximum(BigDecimal maximum) {
+    this.maximum = maximum;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    InterestRange that = (InterestRange) o;
+
+    return minimum != null ? minimum.equals(that.minimum) : that.minimum == null && (maximum != null ? maximum.equals(that.maximum) : that.maximum == null);
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = minimum != null ? minimum.hashCode() : 0;
+    result = 31 * result + (maximum != null ? maximum.hashCode() : 0);
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "InterestRange{" +
+            "minimum=" + minimum +
+            ", maximum=" + maximum +
+            '}';
+  }
+}
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/domain/Product.java b/api/src/main/java/io/mifos/portfolio/api/v1/domain/Product.java
index 6f34f9b..0e7274d 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/domain/Product.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/domain/Product.java
@@ -43,6 +43,9 @@
   @Valid
   private BalanceRange balanceRange;
   @NotNull
+  @Valid
+  private InterestRange interestRange;
+  @NotNull
   private InterestBasis interestBasis;
   @NotNull
   @ValidIdentifier(maxLength = 512)
@@ -101,6 +104,14 @@
     this.balanceRange = balanceRange;
   }
 
+  public InterestRange getInterestRange() {
+    return interestRange;
+  }
+
+  public void setInterestRange(InterestRange interestRange) {
+    this.interestRange = interestRange;
+  }
+
   public InterestBasis getInterestBasis() {
     return interestBasis;
   }
@@ -199,6 +210,7 @@
             Objects.equals(name, product.name) &&
             Objects.equals(termRange, product.termRange) &&
             Objects.equals(balanceRange, product.balanceRange) &&
+            Objects.equals(interestRange, product.interestRange) &&
             interestBasis == product.interestBasis &&
             Objects.equals(patternPackage, product.patternPackage) &&
             Objects.equals(description, product.description) &&
@@ -209,7 +221,7 @@
 
   @Override
   public int hashCode() {
-    return Objects.hash(identifier, name, termRange, balanceRange, interestBasis, patternPackage, description, currencyCode, minorCurrencyUnitDigits, accountAssignments, parameters);
+    return Objects.hash(identifier, name, termRange, balanceRange, interestRange, interestBasis, patternPackage, description, currencyCode, minorCurrencyUnitDigits, accountAssignments, parameters);
   }
 
   @Override
@@ -219,6 +231,7 @@
             ", name='" + name + '\'' +
             ", termRange=" + termRange +
             ", balanceRange=" + balanceRange +
+            ", interestRange=" + interestRange +
             ", interestBasis=" + interestBasis +
             ", patternPackage='" + patternPackage + '\'' +
             ", description='" + description + '\'' +
diff --git a/api/src/test/java/io/mifos/Fixture.java b/api/src/test/java/io/mifos/Fixture.java
index f45d457..5643ecb 100644
--- a/api/src/test/java/io/mifos/Fixture.java
+++ b/api/src/test/java/io/mifos/Fixture.java
@@ -55,6 +55,7 @@
     product.setDescription("Loan for seeds or agricultural equipment");
     product.setTermRange(new TermRange(ChronoUnit.MONTHS, 12));
     product.setBalanceRange(new BalanceRange(fixScale(BigDecimal.ZERO), fixScale(new BigDecimal(10000))));
+    product.setInterestRange(new InterestRange(BigDecimal.valueOf(3_00, 2), BigDecimal.valueOf(12_00, 2)));
     product.setInterestBasis(InterestBasis.CURRENT_BALANCE);
 
     product.setCurrencyCode("XXX");
@@ -114,6 +115,7 @@
     accountAssignments.add(new AccountAssignment(ENTRY, "001-012"));
     ret.setAccountAssignments(accountAssignments);
     ret.setCurrentState(Case.State.CREATED.name());
+    ret.setInterest(BigDecimal.valueOf(10_0000, 4));
 
     final CaseParameters caseParameters = getTestCaseParameters();
     final Gson gson = new Gson();
diff --git a/api/src/test/java/io/mifos/portfolio/api/v1/domain/CaseTest.java b/api/src/test/java/io/mifos/portfolio/api/v1/domain/CaseTest.java
index bc7ecd2..d9aeb46 100644
--- a/api/src/test/java/io/mifos/portfolio/api/v1/domain/CaseTest.java
+++ b/api/src/test/java/io/mifos/portfolio/api/v1/domain/CaseTest.java
@@ -21,6 +21,7 @@
 import org.apache.commons.lang.RandomStringUtils;
 import org.junit.runners.Parameterized;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
 
@@ -61,8 +62,14 @@
             .adjustment(x -> x.setProductIdentifier(null))
             .valid(false));
     ret.add(new ValidationTestCase<Case>("tooLongAccountIdentifier")
-            .adjustment(x -> x.getAccountAssignments().add(new AccountAssignment("x", "0123456789")))
-            .valid(false));
+        .adjustment(x -> x.getAccountAssignments().add(new AccountAssignment("x", "0123456789")))
+        .valid(false));
+    ret.add(new ValidationTestCase<Case>("out of range interest")
+        .adjustment(x -> x.setInterest(BigDecimal.TEN.negate()))
+        .valid(false));
+    ret.add(new ValidationTestCase<Case>("null interest")
+        .adjustment(x -> x.setInterest(BigDecimal.TEN.negate()))
+        .valid(false));
 
     return ret;
   }
diff --git a/api/src/test/java/io/mifos/portfolio/api/v1/domain/InterestRangeTest.java b/api/src/test/java/io/mifos/portfolio/api/v1/domain/InterestRangeTest.java
new file mode 100644
index 0000000..0f4a851
--- /dev/null
+++ b/api/src/test/java/io/mifos/portfolio/api/v1/domain/InterestRangeTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.portfolio.api.v1.domain;
+
+import io.mifos.core.test.domain.ValidationTest;
+import io.mifos.core.test.domain.ValidationTestCase;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author Myrle Krantz
+ */
+@RunWith(Parameterized.class)
+public class InterestRangeTest extends ValidationTest<InterestRange> {
+  public InterestRangeTest(ValidationTestCase<InterestRange> testCase) {
+    super(testCase);
+  }
+
+  @Override
+  protected InterestRange createValidTestSubject() {
+    return new InterestRange(BigDecimal.valueOf(0.02d).setScale(2, BigDecimal.ROUND_UNNECESSARY),
+            BigDecimal.valueOf(0.03d).setScale(2, BigDecimal.ROUND_UNNECESSARY));
+  }
+
+  @Parameterized.Parameters
+  public static Collection testCases() {
+    final Collection<ValidationTestCase> ret = new ArrayList<>();
+    ret.add(new ValidationTestCase<InterestRange>("basicCase")
+            .adjustment(x -> {})
+            .valid(true));
+    ret.add(new ValidationTestCase<InterestRange>("5 and 10")
+            .adjustment(x -> {
+              x.setMinimum(BigDecimal.valueOf(5L).setScale(2, BigDecimal.ROUND_UNNECESSARY));
+              x.setMaximum(BigDecimal.valueOf(10L).setScale(2, BigDecimal.ROUND_UNNECESSARY));
+            })
+            .valid(true));
+    ret.add(new ValidationTestCase<InterestRange>("5 and 5")
+            .adjustment(x -> {
+              x.setMinimum(BigDecimal.valueOf(5L).setScale(2, BigDecimal.ROUND_UNNECESSARY));
+              x.setMaximum(BigDecimal.valueOf(5L).setScale(2, BigDecimal.ROUND_UNNECESSARY));
+            })
+            .valid(true));
+    ret.add(new ValidationTestCase<InterestRange>("maxNull")
+            .adjustment((x) -> x.setMaximum(null))
+            .valid(false));
+    ret.add(new ValidationTestCase<InterestRange>("maximim smaller than minimum")
+            .adjustment(x -> {
+              x.setMinimum(BigDecimal.valueOf(10L).setScale(2, BigDecimal.ROUND_UNNECESSARY));
+              x.setMaximum(BigDecimal.valueOf(5L).setScale(2, BigDecimal.ROUND_UNNECESSARY));
+            })
+            .valid(false));
+    ret.add(new ValidationTestCase<InterestRange>("too large scale")
+            .adjustment(x ->
+                    x.setMinimum(x.getMinimum().setScale(3, BigDecimal.ROUND_UNNECESSARY)))
+            .valid(false));
+    ret.add(new ValidationTestCase<InterestRange>("smaller scale")
+            .adjustment(x ->
+                    x.setMinimum(x.getMinimum().setScale(1, BigDecimal.ROUND_HALF_EVEN)))
+            .valid(true));
+    return ret;
+  }
+}
\ No newline at end of file
diff --git a/api/src/test/java/io/mifos/portfolio/api/v1/domain/ProductTest.java b/api/src/test/java/io/mifos/portfolio/api/v1/domain/ProductTest.java
index 9ab2448..3ab8947 100644
--- a/api/src/test/java/io/mifos/portfolio/api/v1/domain/ProductTest.java
+++ b/api/src/test/java/io/mifos/portfolio/api/v1/domain/ProductTest.java
@@ -80,6 +80,22 @@
     ret.add(new ValidationTestCase<Product>("switchedBalanceRangeMinMax")
             .adjustment(product -> product.setBalanceRange(new BalanceRange(Fixture.fixScale(BigDecimal.TEN), Fixture.fixScale(BigDecimal.ZERO))))
             .valid(false));
+    ret.add(new ValidationTestCase<Product>("nullInterestRange")
+            .adjustment(product -> product.setInterestRange(null))
+            .valid(false));
+    //noinspection BigDecimalMethodWithoutRoundingCalled
+    ret.add(new ValidationTestCase<Product>("switchedInterestRangeMinMax")
+            .adjustment(product -> product.setInterestRange(new InterestRange(BigDecimal.valueOf(200, 2), BigDecimal.valueOf(0.9).setScale(2))))
+            .valid(false));
+    ret.add(new ValidationTestCase<Product>("tooBigMaximumInterestRange")
+            .adjustment(product -> product.setInterestRange(new InterestRange(new BigDecimal("999.99"), new BigDecimal("1000.00"))))
+            .valid(false));
+    ret.add(new ValidationTestCase<Product>("negativeMinimumInterestRange")
+            .adjustment(product -> product.setInterestRange(new InterestRange(BigDecimal.valueOf(-1, 2), BigDecimal.valueOf(1, 2))))
+            .valid(false));
+    ret.add(new ValidationTestCase<Product>("tooManyDigitsAfterTheDecimalInterestRange")
+            .adjustment(product -> product.setInterestRange(new InterestRange(BigDecimal.valueOf(1, 2), new BigDecimal("1.001"))))
+            .valid(false));
     ret.add(new ValidationTestCase<Product>("nullInterestBasis")
             .adjustment(product -> product.setInterestBasis(null))
             .valid(false));
diff --git a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
index adadf68..4c7dfcd 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
@@ -21,6 +21,7 @@
 import io.mifos.core.test.fixture.TenantDataStoreContextTestRule;
 import io.mifos.core.test.listener.EnableEventRecording;
 import io.mifos.core.test.listener.EventRecorder;
+import io.mifos.customer.api.v1.client.CustomerManager;
 import io.mifos.individuallending.api.v1.client.IndividualLending;
 import io.mifos.individuallending.api.v1.domain.product.AccountDesignators;
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
@@ -32,6 +33,7 @@
 import io.mifos.portfolio.service.internal.util.RhythmAdapter;
 import org.junit.*;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -114,6 +116,9 @@
   @MockBean
   LedgerManager ledgerManager;
 
+  @MockBean
+  CustomerManager customerManager;
+
   @SuppressWarnings("SpringAutowiredFieldsWarningInspection")
   @Autowired
   @Qualifier(LOGGER_NAME)
@@ -123,6 +128,7 @@
   public void prepTest() {
     userContext = this.tenantApplicationSecurityEnvironment.createAutoUserContext(TEST_USER);
     AccountingFixture.mockAccountingPrereqs(ledgerManager);
+    Mockito.doReturn(true).when(customerManager).isCustomerInGoodStanding(Fixture.CUSTOMER_IDENTIFIER);
   }
 
   @After
diff --git a/component-test/src/main/java/io/mifos/portfolio/Fixture.java b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
index 5b3785e..f533a12 100644
--- a/component-test/src/main/java/io/mifos/portfolio/Fixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
@@ -41,6 +41,7 @@
   static final int MINOR_CURRENCY_UNIT_DIGITS = 2;
   static final BigDecimal INTEREST_RATE = BigDecimal.valueOf(0.10).setScale(4, RoundingMode.HALF_EVEN);
   static final BigDecimal ACCRUAL_PERIODS = BigDecimal.valueOf(365.2425);
+  public static final String CUSTOMER_IDENTIFIER = "alice";
 
   private static int uniquenessSuffix = 0;
 
@@ -52,6 +53,7 @@
     product.setDescription("Loan for seeds or agricultural equipment");
     product.setTermRange(new TermRange(ChronoUnit.MONTHS, 12));
     product.setBalanceRange(new BalanceRange(fixScale(BigDecimal.ZERO), fixScale(new BigDecimal(10000))));
+    product.setInterestRange(new InterestRange(BigDecimal.valueOf(3_00, 2), BigDecimal.valueOf(12_00, 2)));
     product.setInterestBasis(InterestBasis.CURRENT_BALANCE);
 
     product.setCurrencyCode("XXX");
@@ -116,6 +118,7 @@
     final Set<AccountAssignment> accountAssignments = new HashSet<>();
     ret.setAccountAssignments(accountAssignments);
     ret.setCurrentState(Case.State.CREATED.name());
+    ret.setInterest(BigDecimal.valueOf(10_00, 2));
 
     final CaseParameters caseParameters = getTestCaseParameters();
     final Gson gson = new Gson();
@@ -134,7 +137,7 @@
   {
     final CaseParameters ret = new CaseParameters(generateUniqueIdentifer("fred"));
 
-    ret.setCustomerIdentifier("alice");
+    ret.setCustomerIdentifier(CUSTOMER_IDENTIFIER);
     ret.setMaximumBalance(fixScale(BigDecimal.valueOf(2000L)));
     ret.setTermRange(new TermRange(ChronoUnit.MONTHS, 18));
     ret.setPaymentCycle(new PaymentCycle(ChronoUnit.MONTHS, 1, 1, null, null));
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
index cc2c74c..4ab454a 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
@@ -27,8 +27,10 @@
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
 import io.mifos.individuallending.api.v1.events.IndividualLoanCommandEvent;
 import io.mifos.individuallending.api.v1.events.IndividualLoanEventConstants;
-import io.mifos.portfolio.api.v1.domain.*;
-import io.mifos.portfolio.api.v1.events.ChargeDefinitionEvent;
+import io.mifos.portfolio.api.v1.domain.Case;
+import io.mifos.portfolio.api.v1.domain.CostComponent;
+import io.mifos.portfolio.api.v1.domain.Product;
+import io.mifos.portfolio.api.v1.domain.TaskDefinition;
 import io.mifos.portfolio.api.v1.events.EventConstants;
 import io.mifos.rhythm.spi.v1.client.BeatListener;
 import io.mifos.rhythm.spi.v1.domain.BeatPublish;
@@ -126,13 +128,6 @@
     setFeeToFixedValue(product.getIdentifier(), ChargeIdentifiers.LOAN_ORIGINATION_FEE_ID, LOAN_ORIGINATION_FEE_AMOUNT);
     setFeeToFixedValue(product.getIdentifier(), ChargeIdentifiers.DISBURSEMENT_FEE_ID, DISBURSEMENT_FEE_AMOUNT);
 
-    final ChargeDefinition interestChargeDefinition = portfolioManager.getChargeDefinition(product.getIdentifier(), ChargeIdentifiers.INTEREST_ID);
-    interestChargeDefinition.setAmount(Fixture.INTEREST_RATE);
-
-    portfolioManager.changeChargeDefinition(product.getIdentifier(), interestChargeDefinition.getIdentifier(), interestChargeDefinition);
-    Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_CHARGE_DEFINITION,
-        new ChargeDefinitionEvent(product.getIdentifier(), interestChargeDefinition.getIdentifier())));
-
     taskDefinition = createTaskDefinition(product);
 
     portfolioManager.enableProduct(product.getIdentifier(), true);
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestCases.java b/component-test/src/main/java/io/mifos/portfolio/TestCases.java
index 7493e81..b6afb42 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestCases.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestCases.java
@@ -29,6 +29,7 @@
 import io.mifos.portfolio.api.v1.events.EventConstants;
 import org.junit.Assert;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 import java.math.BigDecimal;
 import java.util.*;
@@ -62,6 +63,13 @@
     }
   }
 
+  @Test(expected = IllegalArgumentException.class)
+  public void shouldFailToCreateCaseWithInterestOutOfRange() throws InterruptedException {
+    final Product product = createAndEnableProduct();
+
+    createAdjustedCase(product.getIdentifier(), x -> x.setInterest(BigDecimal.valueOf(13_0000, 4)));
+  }
+
   @Test
   public void shouldCreateCase() throws InterruptedException {
     final Product product = createAndEnableProduct();
@@ -199,6 +207,23 @@
   }
 
   @Test
+  public void shouldThrowWhenCustomerNotInGoodStanding() throws InterruptedException {
+    Mockito.doReturn(false).when(customerManager).isCustomerInGoodStanding("don");
+
+    final Product product = createProduct();
+
+    final CaseParameters newCaseParameters = Fixture.createAdjustedCaseParameters(x -> x.setCustomerIdentifier("don"));
+    final String originalParameters = new Gson().toJson(newCaseParameters);
+
+    try {
+      createAdjustedCase(product.getIdentifier(), x -> x.setParameters(originalParameters));
+      Assert.fail("This should cause an illegal argument exception because Don is not a customer in good standing.");
+    }
+    catch (final IllegalArgumentException ignored){
+    }
+  }
+
+  @Test
   public void shouldThrowWhenProductNotActivated() throws InterruptedException {
     final Product product = createProduct();
 
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestChargeDefinitions.java b/component-test/src/main/java/io/mifos/portfolio/TestChargeDefinitions.java
index 6673c26..685c183 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestChargeDefinitions.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestChargeDefinitions.java
@@ -62,11 +62,11 @@
         ChargeIdentifiers.DISBURSE_PAYMENT_ID,
         ChargeIdentifiers.TRACK_DISBURSAL_PAYMENT_ID,
         ChargeIdentifiers.TRACK_RETURN_PRINCIPAL_ID,
+        ChargeIdentifiers.INTEREST_ID,
         ChargeIdentifiers.REPAYMENT_ID)
         .collect(Collectors.toSet());
     final Set<String> expectedChangeableChargeDefinitionIdentifiers = Stream.of(
         ChargeIdentifiers.DISBURSEMENT_FEE_ID,
-        ChargeIdentifiers.INTEREST_ID,
         ChargeIdentifiers.LATE_FEE_ID,
         ChargeIdentifiers.LOAN_ORIGINATION_FEE_ID,
         ChargeIdentifiers.PROCESSING_FEE_ID)
@@ -154,24 +154,24 @@
 
 
   @Test
-  public void shouldChangeInterestChargeDefinition() throws InterruptedException {
+  public void shouldChangeDisbursementFeeChargeDefinition() throws InterruptedException {
     final Product product = createProduct();
 
-    final ChargeDefinition interestChargeDefinition
-        = portfolioManager.getChargeDefinition(product.getIdentifier(), ChargeIdentifiers.INTEREST_ID);
-    interestChargeDefinition.setAmount(Fixture.INTEREST_RATE);
+    final ChargeDefinition disbursementFeeDefinition
+        = portfolioManager.getChargeDefinition(product.getIdentifier(), ChargeIdentifiers.DISBURSEMENT_FEE_ID);
+    disbursementFeeDefinition.setAmount(BigDecimal.valueOf(10_0000, 4));
 
     portfolioManager.changeChargeDefinition(
         product.getIdentifier(),
-        interestChargeDefinition.getIdentifier(),
-        interestChargeDefinition);
+        disbursementFeeDefinition.getIdentifier(),
+        disbursementFeeDefinition);
     Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_CHARGE_DEFINITION,
-        new ChargeDefinitionEvent(product.getIdentifier(), interestChargeDefinition.getIdentifier())));
+        new ChargeDefinitionEvent(product.getIdentifier(), disbursementFeeDefinition.getIdentifier())));
 
     final ChargeDefinition chargeDefinitionAsChanged
-        = portfolioManager.getChargeDefinition(product.getIdentifier(), interestChargeDefinition.getIdentifier());
+        = portfolioManager.getChargeDefinition(product.getIdentifier(), disbursementFeeDefinition.getIdentifier());
 
-    Assert.assertEquals(interestChargeDefinition, chargeDefinitionAsChanged);
+    Assert.assertEquals(disbursementFeeDefinition, chargeDefinitionAsChanged);
   }
 
   @Test
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestIndividualLoans.java b/component-test/src/main/java/io/mifos/portfolio/TestIndividualLoans.java
index 62e6f39..0ef6c65 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestIndividualLoans.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestIndividualLoans.java
@@ -26,6 +26,7 @@
 import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPaymentPage;
 import org.junit.Assert;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -37,6 +38,9 @@
 
   @Test
   public void shouldReturnIndividualLoansCases() throws InterruptedException {
+    Mockito.doReturn(true).when(customerManager).isCustomerInGoodStanding("susi");
+    Mockito.doReturn(true).when(customerManager).isCustomerInGoodStanding("george");
+    Mockito.doReturn(true).when(customerManager).isCustomerInGoodStanding("harold");
     final Product product = createAndEnableProduct();
     final Set<String> susisCaseIdentifiers = new HashSet<>();
     final Set<String> georgeCaseIdentifiers = new HashSet<>();
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestProducts.java b/component-test/src/main/java/io/mifos/portfolio/TestProducts.java
index f370f90..876d07a 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestProducts.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestProducts.java
@@ -332,6 +332,7 @@
     product.setDescription(StringUtils.repeat("x", 4096));
     product.setTermRange(new TermRange(ChronoUnit.MONTHS, 12));
     product.setBalanceRange(new BalanceRange(BigDecimal.ZERO.setScale(4, BigDecimal.ROUND_UNNECESSARY), new BigDecimal(10000).setScale(4, BigDecimal.ROUND_UNNECESSARY)));
+    product.setInterestRange(new InterestRange(new BigDecimal("999.98"), new BigDecimal("999.99")));
     product.setInterestBasis(InterestBasis.CURRENT_BALANCE);
     product.setCurrencyCode("XTS");
     product.setMinorCurrencyUnitDigits(4);
diff --git a/service/build.gradle b/service/build.gradle
index f4f79d6..8194a22 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -36,6 +36,7 @@
             [group: 'io.mifos.rhythm', name: 'api', version: versions.mifosrhythm],
             [group: 'io.mifos.accounting', name: 'api', version: versions.frameworkaccounting],
             [group: 'io.mifos.anubis', name: 'library', version: versions.frameworkanubis],
+            [group: 'io.mifos.customer', name: 'api', version: versions.mifoscustomer],
             [group: 'com.google.code.gson', name: 'gson'],
             [group: 'io.mifos.core', name: 'api', version: versions.frameworkapi],
             [group: 'io.mifos.core', name: 'lang', version: versions.frameworklang],
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 07c8d76..9488c60 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.Sets;
 import com.google.gson.Gson;
 import io.mifos.core.lang.ServiceException;
+import io.mifos.customer.api.v1.client.CustomerManager;
 import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
 import io.mifos.individuallending.api.v1.domain.product.ChargeIdentifiers;
 import io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator;
@@ -29,6 +30,7 @@
 import io.mifos.individuallending.internal.repository.CreditWorthinessFactorType;
 import io.mifos.individuallending.internal.service.CostComponentService;
 import io.mifos.individuallending.internal.service.DataContextOfAction;
+import io.mifos.individuallending.internal.service.DataContextService;
 import io.mifos.portfolio.api.v1.domain.Case;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
@@ -57,19 +59,25 @@
 public class IndividualLendingPatternFactory implements PatternFactory {
   final static private String INDIVIDUAL_LENDING_PACKAGE = "io.mifos.individuallending.api.v1";
   private final CaseParametersRepository caseParametersRepository;
+  private final DataContextService dataContextService;
   private final CostComponentService costComponentService;
+  private final CustomerManager customerManager;
   private final IndividualLendingCommandDispatcher individualLendingCommandDispatcher;
   private final Gson gson;
 
   @Autowired
   IndividualLendingPatternFactory(
-          final CaseParametersRepository caseParametersRepository,
-          final CostComponentService costComponentService,
-          final IndividualLendingCommandDispatcher individualLendingCommandDispatcher,
-          @Qualifier(ServiceConstants.GSON_NAME) final Gson gson)
+      final CaseParametersRepository caseParametersRepository,
+      final DataContextService dataContextService,
+      final CostComponentService costComponentService,
+      final CustomerManager customerManager,
+      final IndividualLendingCommandDispatcher individualLendingCommandDispatcher,
+      @Qualifier(ServiceConstants.GSON_NAME) final Gson gson)
   {
     this.caseParametersRepository = caseParametersRepository;
+    this.dataContextService = dataContextService;
     this.costComponentService = costComponentService;
+    this.customerManager = customerManager;
     this.individualLendingCommandDispatcher = individualLendingCommandDispatcher;
     this.gson = gson;
   }
@@ -182,14 +190,15 @@
     final ChargeDefinition interestCharge = charge(
         INTEREST_NAME,
         Action.ACCEPT_PAYMENT,
-        BigDecimal.valueOf(0.05),
+        BigDecimal.ONE,
         CUSTOMER_LOAN,
         INTEREST_INCOME);
     interestCharge.setForCycleSizeUnit(ChronoUnit.YEARS);
     interestCharge.setAccrueAction(Action.APPLY_INTEREST.name());
     interestCharge.setAccrualAccountDesignator(INTEREST_ACCRUAL);
     interestCharge.setProportionalTo(ChargeProportionalDesignator.RUNNING_BALANCE_DESIGNATOR.getValue());
-    interestCharge.setReadOnly(false);
+    interestCharge.setChargeMethod(ChargeDefinition.ChargeMethod.INTEREST);
+    interestCharge.setReadOnly(true);
 
     final ChargeDefinition customerRepaymentCharge = new ChargeDefinition();
     customerRepaymentCharge.setChargeAction(Action.ACCEPT_PAYMENT.name());
@@ -240,9 +249,18 @@
     return ret;
   }
 
+  @Override
+  public void checkParameters(final String parameters) {
+    final CaseParameters caseParameters = gson.fromJson(parameters, CaseParameters.class);
+    final String customerIdentifier = caseParameters.getCustomerIdentifier();
+    if (!customerManager.isCustomerInGoodStanding(customerIdentifier))
+      throw ServiceException.badRequest("Customer ''{0}'' is either not a customer or is not in good standing.");
+  }
+
   @Transactional
   @Override
   public void persistParameters(final Long caseId, final String parameters) {
+    checkParameters(parameters);
     final CaseParameters caseParameters = gson.fromJson(parameters, CaseParameters.class);
     final CaseParametersEntity caseParametersEntity = CaseParametersMapper.map(caseId, caseParameters);
     caseParametersRepository.save(caseParametersEntity);
@@ -277,7 +295,8 @@
 
   @Transactional
   @Override
-  public void changeParameters(Long caseId, String parameters) {
+  public void changeParameters(final Long caseId, final String parameters) {
+    checkParameters(parameters);
     final CaseParameters caseParameters = gson.fromJson(parameters, CaseParameters.class);
     final CaseParametersEntity oldCaseParameters = caseParametersRepository.findByCaseId(caseId)
             .orElseThrow(() -> new IllegalArgumentException("Case id does not represent an individual loan: " + caseId));
@@ -331,7 +350,7 @@
       final Set<String> forAccountDesignators,
       final BigDecimal forPaymentSize) {
     final Action action = Action.valueOf(actionIdentifier);
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(productIdentifier, caseIdentifier, Collections.emptyList());
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(productIdentifier, caseIdentifier, Collections.emptyList());
     final Case.State caseState = Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState());
     checkActionCanBeExecuted(caseState, action);
 
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java b/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
index 36bbe16..00740d5 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
@@ -61,6 +61,7 @@
 @Aggregate
 public class IndividualLoanCommandHandler {
   private final CaseRepository caseRepository;
+  private final DataContextService dataContextService;
   private final CostComponentService costComponentService;
   private final AccountingAdapter accountingAdapter;
   private final TaskInstanceRepository taskInstanceRepository;
@@ -68,10 +69,12 @@
   @Autowired
   public IndividualLoanCommandHandler(
       final CaseRepository caseRepository,
+      final DataContextService dataContextService,
       final CostComponentService costComponentService,
       final AccountingAdapter accountingAdapter,
       final TaskInstanceRepository taskInstanceRepository) {
     this.caseRepository = caseRepository;
+    this.dataContextService = dataContextService;
     this.costComponentService = costComponentService;
     this.accountingAdapter = accountingAdapter;
     this.taskInstanceRepository = taskInstanceRepository;
@@ -85,7 +88,7 @@
   public IndividualLoanCommandEvent process(final OpenCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
             productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.OPEN);
 
@@ -126,7 +129,7 @@
   public IndividualLoanCommandEvent process(final DenyCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.DENY);
 
@@ -162,7 +165,7 @@
   public IndividualLoanCommandEvent process(final ApproveCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.APPROVE);
 
@@ -213,7 +216,7 @@
   public IndividualLoanCommandEvent process(final DisburseCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.DISBURSE);
 
@@ -262,7 +265,7 @@
   public IndividualLoanCommandEvent process(final ApplyInterestCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, null);
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.APPLY_INTEREST);
 
@@ -301,7 +304,7 @@
   public IndividualLoanCommandEvent process(final AcceptPaymentCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.ACCEPT_PAYMENT);
 
@@ -346,7 +349,7 @@
   public IndividualLoanCommandEvent process(final WriteOffCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.WRITE_OFF);
 
@@ -364,7 +367,7 @@
   public IndividualLoanCommandEvent process(final CloseCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.CLOSE);
 
@@ -403,7 +406,7 @@
   public IndividualLoanCommandEvent process(final RecoverCommand command) {
     final String productIdentifier = command.getProductIdentifier();
     final String caseIdentifier = command.getCaseIdentifier();
-    final DataContextOfAction dataContextOfAction = costComponentService.checkedGetDataContext(
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
     IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.RECOVER);
 
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/CostComponentService.java b/service/src/main/java/io/mifos/individuallending/internal/service/CostComponentService.java
index fb76cbc..67725d2 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/CostComponentService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/CostComponentService.java
@@ -20,15 +20,8 @@
 import io.mifos.individuallending.api.v1.domain.product.AccountDesignators;
 import io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator;
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
-import io.mifos.individuallending.internal.mapper.CaseParametersMapper;
-import io.mifos.individuallending.internal.repository.CaseParametersRepository;
-import io.mifos.portfolio.api.v1.domain.AccountAssignment;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
-import io.mifos.portfolio.service.internal.repository.CaseEntity;
-import io.mifos.portfolio.service.internal.repository.CaseRepository;
-import io.mifos.portfolio.service.internal.repository.ProductEntity;
-import io.mifos.portfolio.service.internal.repository.ProductRepository;
 import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import org.javamoney.calc.common.Rate;
 import org.javamoney.moneta.Money;
@@ -54,48 +47,17 @@
   private static final int EXTRA_PRECISION = 4;
   private static final int RUNNING_CALCULATION_PRECISION = 8;
 
-  private final ProductRepository productRepository;
-  private final CaseRepository caseRepository;
-  private final CaseParametersRepository caseParametersRepository;
   private final IndividualLoanService individualLoanService;
   private final AccountingAdapter accountingAdapter;
 
   @Autowired
   public CostComponentService(
-          final ProductRepository productRepository,
-          final CaseRepository caseRepository,
-          final CaseParametersRepository caseParametersRepository,
           final IndividualLoanService individualLoanService,
           final AccountingAdapter accountingAdapter) {
-    this.productRepository = productRepository;
-    this.caseRepository = caseRepository;
-    this.caseParametersRepository = caseParametersRepository;
     this.individualLoanService = individualLoanService;
     this.accountingAdapter = accountingAdapter;
   }
 
-  public DataContextOfAction checkedGetDataContext(
-          final String productIdentifier,
-          final String caseIdentifier,
-          final @Nullable List<AccountAssignment> oneTimeAccountAssignments) {
-
-    final ProductEntity product =
-            productRepository.findByIdentifier(productIdentifier)
-                    .orElseThrow(() -> ServiceException.notFound("Product not found ''{0}''.", productIdentifier));
-    final CaseEntity customerCase =
-            caseRepository.findByProductIdentifierAndIdentifier(productIdentifier, caseIdentifier)
-                    .orElseThrow(() -> ServiceException.notFound("Case not found ''{0}.{1}''.", productIdentifier, caseIdentifier));
-
-    final CaseParameters caseParameters =
-            caseParametersRepository.findByCaseId(customerCase.getId())
-                    .map(x -> CaseParametersMapper.mapEntity(x, product.getMinorCurrencyUnitDigits()))
-                    .orElseThrow(() -> ServiceException.notFound(
-                            "Individual loan not found ''{0}.{1}''.",
-                            productIdentifier, caseIdentifier));
-
-    return new DataContextOfAction(product, customerCase, caseParameters, oneTimeAccountAssignments);
-  }
-
   public CostComponentsForRepaymentPeriod getCostComponentsForAction(
       final Action action,
       final DataContextOfAction dataContextOfAction,
@@ -140,6 +102,7 @@
         caseParameters.getMaximumBalance(),
         BigDecimal.ZERO,
         BigDecimal.ZERO,
+        dataContextOfAction.getInterestAsFraction(),
         minorCurrencyUnitDigits,
         true);
   }
@@ -158,6 +121,7 @@
         caseParameters.getMaximumBalance(),
         BigDecimal.ZERO,
         BigDecimal.ZERO,
+        dataContextOfAction.getInterestAsFraction(),
         minorCurrencyUnitDigits,
         true);
   }
@@ -177,6 +141,7 @@
         caseParameters.getMaximumBalance(),
         BigDecimal.ZERO,
         BigDecimal.ZERO,
+        dataContextOfAction.getInterestAsFraction(),
         minorCurrencyUnitDigits,
         true);
   }
@@ -233,6 +198,7 @@
         caseParameters.getMaximumBalance(),
         currentBalance,
         disbursalSize,
+        dataContextOfAction.getInterestAsFraction(),
         minorCurrencyUnitDigits,
         true);
   }
@@ -272,6 +238,7 @@
         caseParameters.getMaximumBalance(),
         currentBalance,
         BigDecimal.ZERO,
+        dataContextOfAction.getInterestAsFraction(),
         minorCurrencyUnitDigits,
         true);
   }
@@ -307,7 +274,11 @@
       final List<ScheduledCharge> hypotheticalScheduledCharges = individualLoanService.getScheduledCharges(
           productIdentifier,
           hypotheticalScheduledActions);
-      loanPaymentSize = getLoanPaymentSize(currentBalance, minorCurrencyUnitDigits, hypotheticalScheduledCharges);
+      loanPaymentSize = getLoanPaymentSize(
+          currentBalance,
+          dataContextOfAction.getInterestAsFraction(),
+          minorCurrencyUnitDigits,
+          hypotheticalScheduledCharges);
     }
 
     final List<ScheduledCharge> scheduledChargesForThisAction = individualLoanService.getScheduledCharges(
@@ -331,6 +302,7 @@
         caseParameters.getMaximumBalance(),
         currentBalance,
         loanPaymentSize,
+        dataContextOfAction.getInterestAsFraction(),
         minorCurrencyUnitDigits,
         true);
   }
@@ -414,6 +386,7 @@
         caseParameters.getMaximumBalance(),
         currentBalance,
         BigDecimal.ZERO,
+        dataContextOfAction.getInterestAsFraction(),
         minorCurrencyUnitDigits,
         true);
   }
@@ -436,6 +409,7 @@
       final BigDecimal maximumBalance,
       final BigDecimal runningBalance,
       final BigDecimal entryAccountAdjustment, //disbursement or payment size.
+      final BigDecimal interest,
       final int minorCurrencyUnitDigits,
       final boolean accrualAccounting) {
     final Map<String, BigDecimal> balanceAdjustments = new HashMap<>();
@@ -470,7 +444,7 @@
         final CostComponent costComponent = costComponentMap
             .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
 
-        final BigDecimal chargeAmount = howToApplyScheduledChargeToAmount(scheduledCharge)
+        final BigDecimal chargeAmount = howToApplyScheduledChargeToAmount(scheduledCharge, interest)
             .apply(amountProportionalTo)
             .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
         adjustBalances(
@@ -526,34 +500,42 @@
   }
 
   private static Optional<ChargeProportionalDesignator> proportionalToDesignator(final ScheduledCharge scheduledCharge) {
-    if (!scheduledCharge.getChargeDefinition().getChargeMethod().equals(ChargeDefinition.ChargeMethod.PROPORTIONAL))
+    if (!scheduledCharge.getChargeDefinition().getChargeMethod().equals(ChargeDefinition.ChargeMethod.PROPORTIONAL) &&
+        !scheduledCharge.getChargeDefinition().getChargeMethod().equals(ChargeDefinition.ChargeMethod.INTEREST))
       return Optional.of(ChargeProportionalDesignator.NOT_PROPORTIONAL);
 
     return ChargeProportionalDesignator.fromString(scheduledCharge.getChargeDefinition().getProportionalTo());
   }
 
   private static Function<BigDecimal, BigDecimal> howToApplyScheduledChargeToAmount(
-      final ScheduledCharge scheduledCharge)
+      final ScheduledCharge scheduledCharge, final BigDecimal interest)
   {
     switch (scheduledCharge.getChargeDefinition().getChargeMethod())
     {
-      case FIXED:
+      case FIXED: {
         return (amountProportionalTo) -> scheduledCharge.getChargeDefinition().getAmount();
-      case PROPORTIONAL:
-        return (amountProportionalTo) ->
-            PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
-                .multiply(amountProportionalTo);
-      default:
+      }
+      case PROPORTIONAL: {
+        final BigDecimal chargeAmountPerPeriod = PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, scheduledCharge.getChargeDefinition().getAmount(), RUNNING_CALCULATION_PRECISION);
+        return chargeAmountPerPeriod::multiply;
+      }
+      case INTEREST: {
+        final BigDecimal chargeAmountPerPeriod = PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, interest, RUNNING_CALCULATION_PRECISION);
+        return chargeAmountPerPeriod::multiply;
+      }
+      default: {
         return (amountProportionalTo) -> BigDecimal.ZERO;
+      }
     }
   }
 
   static BigDecimal getLoanPaymentSize(final BigDecimal startingBalance,
+                                       final BigDecimal interest,
                                        final int minorCurrencyUnitDigits,
                                        final List<ScheduledCharge> scheduledCharges) {
     final int precision = startingBalance.precision() + minorCurrencyUnitDigits + EXTRA_PRECISION;
     final Map<Period, BigDecimal> accrualRatesByPeriod
-        = PeriodChargeCalculator.getPeriodAccrualInterestRate(scheduledCharges, precision);
+        = PeriodChargeCalculator.getPeriodAccrualInterestRate(interest, scheduledCharges, precision);
 
     final int periodCount = accrualRatesByPeriod.size();
     if (periodCount == 0)
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/DataContextOfAction.java b/service/src/main/java/io/mifos/individuallending/internal/service/DataContextOfAction.java
index f7c2485..af636fc 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/DataContextOfAction.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/DataContextOfAction.java
@@ -23,6 +23,7 @@
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import java.math.BigDecimal;
 import java.util.Collections;
 import java.util.List;
 
@@ -34,6 +35,7 @@
   private final CaseEntity customerCase;
   private final CaseParameters caseParameters;
   private final List<AccountAssignment> oneTimeAccountAssignments;
+  private final BigDecimal interestAsFraction;
 
   DataContextOfAction(final @Nonnull ProductEntity product,
                       final @Nonnull CaseEntity customerCase,
@@ -43,6 +45,7 @@
     this.customerCase = customerCase;
     this.caseParameters = caseParameters;
     this.oneTimeAccountAssignments = oneTimeAccountAssignments == null ? Collections.emptyList() : oneTimeAccountAssignments;
+    interestAsFraction = customerCase.getInterest().divide(BigDecimal.valueOf(100), 4, BigDecimal.ROUND_HALF_EVEN);;
   }
 
   public @Nonnull ProductEntity getProduct() {
@@ -68,4 +71,8 @@
   public String getMessageForCharge(final Action action) {
     return getCompoundIdentifer() + "." + action.name();
   }
+
+  BigDecimal getInterestAsFraction() {
+    return interestAsFraction;
+  }
 }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/DataContextService.java b/service/src/main/java/io/mifos/individuallending/internal/service/DataContextService.java
new file mode 100644
index 0000000..225d0a6
--- /dev/null
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/DataContextService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * Licensed 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 io.mifos.individuallending.internal.service;
+
+import io.mifos.core.lang.ServiceException;
+import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
+import io.mifos.individuallending.internal.mapper.CaseParametersMapper;
+import io.mifos.individuallending.internal.repository.CaseParametersRepository;
+import io.mifos.portfolio.api.v1.domain.AccountAssignment;
+import io.mifos.portfolio.service.internal.repository.CaseEntity;
+import io.mifos.portfolio.service.internal.repository.CaseRepository;
+import io.mifos.portfolio.service.internal.repository.ProductEntity;
+import io.mifos.portfolio.service.internal.repository.ProductRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+/**
+ * @author Myrle Krantz
+ */
+@Service
+public class DataContextService {
+  private final ProductRepository productRepository;
+  private final CaseRepository caseRepository;
+  private final CaseParametersRepository caseParametersRepository;
+
+  @Autowired
+  public DataContextService(
+      final ProductRepository productRepository,
+      final CaseRepository caseRepository,
+      final CaseParametersRepository caseParametersRepository) {
+    this.productRepository = productRepository;
+    this.caseRepository = caseRepository;
+    this.caseParametersRepository = caseParametersRepository;
+  }
+
+  public DataContextOfAction checkedGetDataContext(
+      final String productIdentifier,
+      final String caseIdentifier,
+      final @Nullable List<AccountAssignment> oneTimeAccountAssignments) {
+
+    final ProductEntity product =
+        productRepository.findByIdentifier(productIdentifier)
+            .orElseThrow(() -> ServiceException.notFound("Product not found ''{0}''.", productIdentifier));
+    final CaseEntity customerCase =
+        caseRepository.findByProductIdentifierAndIdentifier(productIdentifier, caseIdentifier)
+            .orElseThrow(() -> ServiceException.notFound("Case not found ''{0}.{1}''.", productIdentifier, caseIdentifier));
+
+    final CaseParameters caseParameters =
+        caseParametersRepository.findByCaseId(customerCase.getId())
+            .map(x -> CaseParametersMapper.mapEntity(x, product.getMinorCurrencyUnitDigits()))
+            .orElseThrow(() -> ServiceException.notFound(
+                "Individual loan not found ''{0}.{1}''.",
+                productIdentifier, caseIdentifier));
+
+    return new DataContextOfAction(product, customerCase, caseParameters, oneTimeAccountAssignments);
+  }
+}
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/IndividualLoanService.java b/service/src/main/java/io/mifos/individuallending/internal/service/IndividualLoanService.java
index e7423fb..0f77033 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/IndividualLoanService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/IndividualLoanService.java
@@ -15,15 +15,12 @@
  */
 package io.mifos.individuallending.internal.service;
 
-import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
 import io.mifos.individuallending.api.v1.domain.caseinstance.ChargeName;
 import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPayment;
 import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPaymentPage;
 import io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
-import io.mifos.portfolio.api.v1.domain.Product;
 import io.mifos.portfolio.service.internal.service.ChargeDefinitionService;
-import io.mifos.portfolio.service.internal.service.ProductService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -40,40 +37,36 @@
  */
 @Service
 public class IndividualLoanService {
-  private final ProductService productService;
   private final ChargeDefinitionService chargeDefinitionService;
 
   @Autowired
-  public IndividualLoanService(final ProductService productService,
-                               final ChargeDefinitionService chargeDefinitionService) {
-    this.productService = productService;
+  public IndividualLoanService(final ChargeDefinitionService chargeDefinitionService) {
     this.chargeDefinitionService = chargeDefinitionService;
   }
 
   public PlannedPaymentPage getPlannedPaymentsPage(
-          final String productIdentifier,
-          final CaseParameters caseParameters,
-          final int pageIndex,
-          final int size,
-          final @Nonnull LocalDate initialDisbursalDate) {
-    final Product product = productService.findByIdentifier(productIdentifier)
-            .orElseThrow(() -> new IllegalArgumentException("Non-existent product identifier."));
-    final int minorCurrencyUnitDigits = product.getMinorCurrencyUnitDigits();
+      final DataContextOfAction dataContextOfAction,
+      final int pageIndex,
+      final int size,
+      final @Nonnull LocalDate initialDisbursalDate) {
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
 
-    final List<ScheduledAction> scheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(initialDisbursalDate, caseParameters);
+    final List<ScheduledAction> scheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(initialDisbursalDate, dataContextOfAction.getCaseParameters());
 
-    final List<ScheduledCharge> scheduledCharges = getScheduledCharges(productIdentifier, scheduledActions);
+    final List<ScheduledCharge> scheduledCharges = getScheduledCharges(dataContextOfAction.getProduct().getIdentifier(), scheduledActions);
 
     final BigDecimal loanPaymentSize = CostComponentService.getLoanPaymentSize(
-        caseParameters.getMaximumBalance(),
+        dataContextOfAction.getCaseParameters().getMaximumBalance(),
+        dataContextOfAction.getInterestAsFraction(),
         minorCurrencyUnitDigits,
         scheduledCharges);
 
     final List<PlannedPayment> plannedPaymentsElements = getPlannedPaymentsElements(
-        caseParameters.getMaximumBalance(),
+        dataContextOfAction.getCaseParameters().getMaximumBalance(),
         minorCurrencyUnitDigits,
         scheduledCharges,
-        loanPaymentSize);
+        loanPaymentSize,
+        dataContextOfAction.getInterestAsFraction());
 
     final Set<ChargeName> chargeNames = scheduledCharges.stream()
             .map(IndividualLoanService::chargeNameFromChargeDefinition)
@@ -124,7 +117,8 @@
       final BigDecimal initialBalance,
       final int minorCurrencyUnitDigits,
       final List<ScheduledCharge> scheduledCharges,
-      final BigDecimal loanPaymentSize) {
+      final BigDecimal loanPaymentSize,
+      final BigDecimal interest) {
     final Map<Period, SortedSet<ScheduledCharge>> orderedScheduledChargesGroupedByPeriod
         = scheduledCharges.stream()
         .collect(Collectors.groupingBy(IndividualLoanService::getPeriodFromScheduledCharge,
@@ -164,6 +158,7 @@
                   initialBalance,
                   balance,
                   currentLoanPaymentSize,
+                  interest,
                   minorCurrencyUnitDigits,
                   false);
 
@@ -185,7 +180,7 @@
       return scheduledAction.repaymentPeriod;
   }
 
-  private List<ScheduledCharge> getScheduledCharges(final List<ScheduledAction> scheduledActions,
+  static List<ScheduledCharge> getScheduledCharges(final List<ScheduledAction> scheduledActions,
                                                     final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction,
                                                     final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction) {
     return scheduledActions.stream()
@@ -198,7 +193,7 @@
         .collect(Collectors.toList());
   }
 
-  private Stream<ChargeDefinition> getChargeDefinitionStream(
+  private static Stream<ChargeDefinition> getChargeDefinitionStream(
           final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction,
           final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction,
           final ScheduledAction scheduledAction) {
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/PeriodChargeCalculator.java b/service/src/main/java/io/mifos/individuallending/internal/service/PeriodChargeCalculator.java
index 58cac97..39bb325 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/PeriodChargeCalculator.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/PeriodChargeCalculator.java
@@ -38,12 +38,14 @@
   {
   }
 
-  static Map<Period, BigDecimal> getPeriodAccrualInterestRate(final List<ScheduledCharge> scheduledCharges,
-                                                              final int precision) {
+  static Map<Period, BigDecimal> getPeriodAccrualInterestRate(
+      final BigDecimal interest,
+      final List<ScheduledCharge> scheduledCharges,
+      final int precision) {
     return scheduledCharges.stream()
             .filter(PeriodChargeCalculator::accruedInterestCharge)
             .collect(Collectors.groupingBy(scheduledCharge -> scheduledCharge.getScheduledAction().repaymentPeriod,
-                    Collectors.mapping(x -> chargeAmountPerPeriod(x, precision), RateCollectors.compound(precision))));
+                    Collectors.mapping(x -> chargeAmountPerPeriod(x, interest, precision), RateCollectors.compound(precision))));
   }
 
   private static boolean accruedInterestCharge(final ScheduledCharge scheduledCharge)
@@ -52,15 +54,16 @@
         scheduledCharge.getChargeDefinition().getAccrueAction() != null &&
         scheduledCharge.getChargeDefinition().getAccrueAction().equals(Action.APPLY_INTEREST.name()) &&
         scheduledCharge.getScheduledAction().action == Action.ACCEPT_PAYMENT &&
-        scheduledCharge.getScheduledAction().actionPeriod != null;
+        scheduledCharge.getScheduledAction().actionPeriod != null &&
+        scheduledCharge.getChargeDefinition().getChargeMethod() == ChargeDefinition.ChargeMethod.INTEREST;
   }
 
-  static BigDecimal chargeAmountPerPeriod(final ScheduledCharge scheduledCharge, final int precision)
+  static BigDecimal chargeAmountPerPeriod(final ScheduledCharge scheduledCharge, final BigDecimal amount, final int precision)
   {
     final ChargeDefinition chargeDefinition = scheduledCharge.getChargeDefinition();
     final ScheduledAction scheduledAction = scheduledCharge.getScheduledAction();
     if (chargeDefinition.getForCycleSizeUnit() == null)
-      return chargeDefinition.getAmount();
+      return amount;
 
     final BigDecimal actionPeriodDuration
         = BigDecimal.valueOf(
@@ -84,7 +87,7 @@
     final int accrualPeriodsInActionPeriod = actionPeriodDuration.divide(
         accrualPeriodDuration.orElse(actionPeriodDuration), precision, BigDecimal.ROUND_HALF_EVEN)
         .intValueExact();
-    final BigDecimal rateForAccrualPeriod = chargeDefinition.getAmount().divide(
+    final BigDecimal rateForAccrualPeriod = amount.divide(
         accrualPeriodsInCycle, precision, BigDecimal.ROUND_HALF_EVEN);
     return createCompoundedRate(rateForAccrualPeriod, accrualPeriodsInActionPeriod, precision);
   }
diff --git a/service/src/main/java/io/mifos/individuallending/rest/PlannedPaymentsRestController.java b/service/src/main/java/io/mifos/individuallending/rest/PlannedPaymentsRestController.java
index 3df4fab..4d3dce0 100644
--- a/service/src/main/java/io/mifos/individuallending/rest/PlannedPaymentsRestController.java
+++ b/service/src/main/java/io/mifos/individuallending/rest/PlannedPaymentsRestController.java
@@ -18,12 +18,11 @@
 
 import io.mifos.anubis.annotation.AcceptedTokenType;
 import io.mifos.anubis.annotation.Permittable;
-import io.mifos.individuallending.internal.service.CaseParametersService;
-import io.mifos.individuallending.internal.service.IndividualLoanService;
 import io.mifos.core.lang.DateConverter;
-import io.mifos.core.lang.ServiceException;
-import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
 import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPaymentPage;
+import io.mifos.individuallending.internal.service.DataContextOfAction;
+import io.mifos.individuallending.internal.service.DataContextService;
+import io.mifos.individuallending.internal.service.IndividualLoanService;
 import io.mifos.portfolio.api.v1.PermittableGroupIds;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
@@ -31,6 +30,7 @@
 
 import java.time.LocalDate;
 import java.time.ZoneId;
+import java.util.Collections;
 
 /**
  * @author Myrle Krantz
@@ -39,14 +39,14 @@
 @RestController
 @RequestMapping("/individuallending/products/{productidentifier}/cases/{caseidentifier}/plannedpayments")
 public class PlannedPaymentsRestController {
-  private final CaseParametersService caseParametersService;
+  private final DataContextService dataContextService;
   private final IndividualLoanService individualLoanService;
 
   @Autowired
   public PlannedPaymentsRestController(
-          final CaseParametersService caseParametersService,
-          final IndividualLoanService individualLoanService) {
-    this.caseParametersService = caseParametersService;
+      final DataContextService dataContextService,
+      final IndividualLoanService individualLoanService) {
+    this.dataContextService = dataContextService;
     this.individualLoanService = individualLoanService;
   }
 
@@ -63,11 +63,7 @@
           @RequestParam(value = "size", required = false) final Integer size,
           @RequestParam(value = "initialDisbursalDate", required = false) final String initialDisbursalDate)
   {
-    final CaseParameters caseParameters = caseParametersService
-            .findByIdentifier(productIdentifier, caseIdentifier)
-            .orElseThrow(() -> ServiceException.notFound(
-                    "Instance with identifier " + productIdentifier + "." + caseIdentifier + " doesn't exist or it is not an individual loan."));
-
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(productIdentifier, caseIdentifier, Collections.emptyList());
 
     final LocalDate parsedInitialDisbursalDate = initialDisbursalDate == null
             ? LocalDate.now(ZoneId.of("UTC"))
@@ -75,6 +71,6 @@
     final Integer pageIndexToUse = pageIndex != null ? pageIndex : 0;
     final Integer sizeToUse = size != null ? size : 20;
 
-    return individualLoanService.getPlannedPaymentsPage(productIdentifier, caseParameters, pageIndexToUse, sizeToUse, parsedInitialDisbursalDate);
+    return individualLoanService.getPlannedPaymentsPage(dataContextOfAction, pageIndexToUse, sizeToUse, parsedInitialDisbursalDate);
   }
 }
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
index adcb3b3..9668332 100644
--- a/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
+++ b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
@@ -25,6 +25,7 @@
 import io.mifos.core.lang.config.EnableServiceException;
 import io.mifos.core.lang.config.EnableTenantContext;
 import io.mifos.core.mariadb.config.EnableMariaDB;
+import io.mifos.customer.api.v1.client.CustomerManager;
 import io.mifos.individuallending.IndividualLendingConfiguration;
 import io.mifos.portfolio.service.ServiceConstants;
 import io.mifos.rhythm.api.v1.client.RhythmManager;
@@ -60,7 +61,7 @@
 })
 @EnableJpaRepositories(basePackages = "io.mifos.portfolio.service.internal.repository")
 @EntityScan(basePackages = "io.mifos.portfolio.service.internal.repository")
-@EnableFeignClients(clients = {LedgerManager.class, RhythmManager.class})
+@EnableFeignClients(clients = {LedgerManager.class, RhythmManager.class, CustomerManager.class})
 @RibbonClient(name = "portfolio-v1")
 @EnableApplicationName
 @Import(IndividualLendingConfiguration.class)
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/checker/CaseChecker.java b/service/src/main/java/io/mifos/portfolio/service/internal/checker/CaseChecker.java
new file mode 100644
index 0000000..11b422f
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/checker/CaseChecker.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * Licensed 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 io.mifos.portfolio.service.internal.checker;
+
+import io.mifos.core.lang.ServiceException;
+import io.mifos.portfolio.api.v1.domain.Case;
+import io.mifos.portfolio.api.v1.domain.InterestRange;
+import io.mifos.portfolio.api.v1.domain.Product;
+import io.mifos.portfolio.service.internal.pattern.PatternFactoryRegistry;
+import io.mifos.portfolio.service.internal.repository.ProductEntity;
+import io.mifos.portfolio.service.internal.repository.ProductRepository;
+import io.mifos.portfolio.service.internal.service.CaseService;
+import io.mifos.portfolio.service.internal.service.ProductService;
+import io.mifos.products.spi.PatternFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.Optional;
+
+/**
+ * @author Myrle Krantz
+ */
+@Component
+public class CaseChecker {
+  private final CaseService caseService;
+  private final ProductService productService;
+  private final ProductRepository productRepository;
+  private final PatternFactoryRegistry patternFactoryRegistry;
+
+  @Autowired
+  public CaseChecker(final CaseService caseService,
+                     final ProductService productService,
+                     final ProductRepository productRepository,
+                     final PatternFactoryRegistry patternFactoryRegistry) {
+    this.caseService = caseService;
+    this.productService = productService;
+    this.productRepository = productRepository;
+    this.patternFactoryRegistry = patternFactoryRegistry;
+  }
+
+  public void checkForCreate(final String productIdentifier, final Case instance) {
+    caseService.findByIdentifier(productIdentifier, instance.getIdentifier())
+        .ifPresent(x -> {throw ServiceException.conflict("Duplicate identifier: " + productIdentifier + "." + x.getIdentifier());});
+
+    final Optional<Boolean> productEnabled = productService.findEnabledByIdentifier(productIdentifier);
+    if (!productEnabled.orElseThrow(() -> ServiceException.internalError("Product should exist, but doesn't"))) {
+      throw ServiceException.badRequest("Product must be enabled before cases for it can be created: " + productIdentifier);}
+
+    checkForChange(productIdentifier, instance);
+  }
+
+  public void checkForChange(final String productIdentifier, final Case instance) {
+    final Product product = productService.findByIdentifier(productIdentifier)
+        .orElseThrow(() -> ServiceException.badRequest("Product must exist ''{0}''.", productIdentifier));
+    final InterestRange interestRange = product.getInterestRange();
+
+    final BigDecimal interest = instance.getInterest();
+    if (interest.compareTo(interestRange.getMinimum()) < 0 ||
+        interest.compareTo(interestRange.getMaximum()) > 0)
+      throw ServiceException.badRequest("Interest for the case ({0}) must be within the range defined by the product ({1}).", interest, interestRange.toString());
+
+    getPatternFactory(productIdentifier).checkParameters(instance.getParameters());
+  }
+
+  private PatternFactory getPatternFactory(final String productIdentifier) {
+    return productRepository.findByIdentifier(productIdentifier)
+        .map(ProductEntity::getPatternPackage)
+        .flatMap(patternFactoryRegistry::getPatternFactoryForPackage)
+        .orElseThrow(() -> new IllegalArgumentException("Case references unsupported product type."));
+  }
+}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/CaseCommandHandler.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/CaseCommandHandler.java
index a8700c0..aa91b80 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/CaseCommandHandler.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/CaseCommandHandler.java
@@ -80,7 +80,7 @@
     return new CaseEvent(caseInstance.getProductIdentifier(), caseInstance.getIdentifier());
   }
 
-  private PatternFactory getPatternFactory(String productIdentifier) {
+  private PatternFactory getPatternFactory(final String productIdentifier) {
     return productRepository.findByIdentifier(productIdentifier)
               .map(ProductEntity::getPatternPackage)
               .flatMap(patternFactoryRegistry::getPatternFactoryForPackage)
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/CaseMapper.java b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/CaseMapper.java
index e6ece90..2ffcbe8 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/CaseMapper.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/CaseMapper.java
@@ -37,6 +37,7 @@
 
     ret.setIdentifier(instance.getIdentifier());
     ret.setProductIdentifier(instance.getProductIdentifier());
+    ret.setInterest(instance.getInterest());
     ret.setParameters(parameters);
     ret.setAccountAssignments(instance.getAccountAssignments().stream().map(CaseMapper::mapAccountAssignmentEntity).collect(Collectors.toSet()));
     ret.setCurrentState(instance.getCurrentState());
@@ -62,6 +63,7 @@
 
     ret.setIdentifier(instance.getIdentifier());
     ret.setProductIdentifier(instance.getProductIdentifier());
+    ret.setInterest(instance.getInterest());
     ret.setAccountAssignments(instance.getAccountAssignments().stream()
         .map(x -> CaseMapper.map(x, ret))
         .collect(Collectors.toSet()));
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/ProductMapper.java b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/ProductMapper.java
index 40d85d8..4e470b3 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/ProductMapper.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/ProductMapper.java
@@ -43,6 +43,8 @@
                     productEntity.getTermRangeMaximum()));
     product.setBalanceRange(
             new BalanceRange(productEntity.getBalanceRangeMinimum().setScale(productEntity.getMinorCurrencyUnitDigits(), BigDecimal.ROUND_HALF_EVEN), productEntity.getBalanceRangeMaximum().setScale(productEntity.getMinorCurrencyUnitDigits(), BigDecimal.ROUND_HALF_EVEN)));
+    product.setInterestRange(
+            new InterestRange(productEntity.getInterestRangeMinimum(), productEntity.getInterestRangeMaximum()));
     product.setInterestBasis(productEntity.getInterestBasis());
     product.setPatternPackage(productEntity.getPatternPackage());
     product.setDescription(productEntity.getDescription());
@@ -70,8 +72,8 @@
     ret.setTermRangeMaximum(product.getTermRange().getMaximum());
     ret.setBalanceRangeMinimum(product.getBalanceRange().getMinimum());
     ret.setBalanceRangeMaximum(product.getBalanceRange().getMaximum());
-    ret.setInterestRangeMinimum(BigDecimal.ZERO);
-    ret.setInterestRangeMaximum(BigDecimal.valueOf(999.99));
+    ret.setInterestRangeMinimum(product.getInterestRange().getMinimum());
+    ret.setInterestRangeMaximum(product.getInterestRange().getMaximum());
     ret.setInterestBasis(product.getInterestBasis());
     ret.setPatternPackage(product.getPatternPackage());
     ret.setDescription(product.getDescription());
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseEntity.java b/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseEntity.java
index 83e023f..39a4ee7 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseEntity.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseEntity.java
@@ -19,6 +19,7 @@
 
 import javax.annotation.Nullable;
 import javax.persistence.*;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.Objects;
 import java.util.Set;
@@ -41,6 +42,9 @@
   @Column(name = "product_identifier", nullable = false)
   private String productIdentifier;
 
+  @Column(name = "interest")
+  private BigDecimal interest;
+
   @OneToMany(targetEntity = CaseAccountAssignmentEntity.class, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "caseEntity")
   private Set<CaseAccountAssignmentEntity> accountAssignments;
 
@@ -95,6 +99,14 @@
     this.productIdentifier = productIdentifier;
   }
 
+  public BigDecimal getInterest() {
+    return interest;
+  }
+
+  public void setInterest(BigDecimal interest) {
+    this.interest = interest;
+  }
+
   public Set<CaseAccountAssignmentEntity> getAccountAssignments() {
     return accountAssignments;
   }
diff --git a/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java b/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
index f2a4ed5..3117f67 100644
--- a/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
+++ b/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
@@ -25,6 +25,7 @@
 import io.mifos.portfolio.api.v1.domain.CasePage;
 import io.mifos.portfolio.api.v1.domain.Command;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
+import io.mifos.portfolio.service.internal.checker.CaseChecker;
 import io.mifos.portfolio.service.internal.command.ChangeCaseCommand;
 import io.mifos.portfolio.service.internal.command.CreateCaseCommand;
 import io.mifos.portfolio.service.internal.service.CaseService;
@@ -40,7 +41,6 @@
 import javax.validation.Valid;
 import java.math.BigDecimal;
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -53,17 +53,21 @@
 
   private final CommandGateway commandGateway;
   private final CaseService caseService;
+  private final CaseChecker caseChecker;
   private final ProductService productService;
   private final TaskInstanceService taskInstanceService;
 
-  @Autowired public CaseRestController(
+  @Autowired
+  public CaseRestController(
       final CommandGateway commandGateway,
       final CaseService caseService,
+      final CaseChecker caseChecker,
       final ProductService productService,
       final TaskInstanceService taskInstanceService) {
     super();
     this.commandGateway = commandGateway;
     this.caseService = caseService;
+    this.caseChecker = caseChecker;
     this.productService = productService;
     this.taskInstanceService = taskInstanceService;
   }
@@ -94,13 +98,6 @@
   {
     checkThatProductExists(productIdentifier);
 
-    caseService.findByIdentifier(productIdentifier, instance.getIdentifier())
-            .ifPresent(x -> {throw ServiceException.conflict("Duplicate identifier: " + productIdentifier + "." + x.getIdentifier());});
-
-    final Optional<Boolean> productEnabled = productService.findEnabledByIdentifier(productIdentifier);
-    if (!productEnabled.orElseThrow(() -> ServiceException.internalError("Product should exist, but doesn't"))) {
-      throw ServiceException.badRequest("Product must be enabled before cases for it can be created: " + productIdentifier);}
-
     if (!instance.getProductIdentifier().equals(productIdentifier))
       throw ServiceException.badRequest("Product identifier in request body must match product identifier in request path.");
 
@@ -120,6 +117,8 @@
     if (instance.getLastModifiedOn() != null)
       throw ServiceException.badRequest("LastModifiedOn must 'null' be upon initial creation.");
 
+    caseChecker.checkForCreate(productIdentifier, instance);
+
     this.commandGateway.process(new CreateCaseCommand(instance));
     return new ResponseEntity<>(HttpStatus.ACCEPTED);
   }
@@ -159,6 +158,8 @@
     if (!caseIdentifier.equals(instance.getIdentifier()))
       throw ServiceException.badRequest("Instance identifier may not be changed.");
 
+    caseChecker.checkForChange(productIdentifier, instance);
+
     this.commandGateway.process(new ChangeCaseCommand(instance));
     return new ResponseEntity<>(HttpStatus.ACCEPTED);
     //TODO: Make sure case can't be changed from certain states.
@@ -245,5 +246,5 @@
       throw ServiceException.notFound("Product with identifier ''{0}'' doesn''t exist.", productIdentifier);
   }
 
-  //TODO: check that case parameters are within product parameters in put and post.
+  //TODO: createCheck that case parameters are within product parameters in put and post.
 }
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/products/spi/PatternFactory.java b/service/src/main/java/io/mifos/products/spi/PatternFactory.java
index 5357e39..4b9f3c2 100644
--- a/service/src/main/java/io/mifos/products/spi/PatternFactory.java
+++ b/service/src/main/java/io/mifos/products/spi/PatternFactory.java
@@ -32,6 +32,7 @@
 public interface PatternFactory {
   Pattern pattern();
   List<ChargeDefinition> charges();
+  void checkParameters(String parameters);
   void persistParameters(Long caseId, String parameters);
   void changeParameters(Long caseId, String parameters);
   Optional<String> getParameters(Long caseId, int minorCurrencyUnitDigits);
diff --git a/service/src/main/resources/db/migrations/mariadb/V6__interest_and_charges.sql b/service/src/main/resources/db/migrations/mariadb/V6__interest_and_charges.sql
new file mode 100644
index 0000000..78e109a
--- /dev/null
+++ b/service/src/main/resources/db/migrations/mariadb/V6__interest_and_charges.sql
@@ -0,0 +1,17 @@
+--
+-- Copyright 2017 Kuelap, Inc.
+--
+-- Licensed 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.
+--
+
+ALTER TABLE bastet_cases ADD COLUMN interest DECIMAL(5,2) NULL DEFAULT NULL;
\ No newline at end of file
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/Fixture.java b/service/src/test/java/io/mifos/individuallending/internal/service/Fixture.java
index c649180..7f73106 100644
--- a/service/src/test/java/io/mifos/individuallending/internal/service/Fixture.java
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/Fixture.java
@@ -86,7 +86,6 @@
   }
 
   static ScheduledCharge scheduledInterestBookingCharge(
-      final double amount,
       final LocalDate initialDate,
       final int chargeDateDelta,
       final int periodBeginDelta,
@@ -99,12 +98,12 @@
         new Period(chargeDate, periodLength),
         getPeriod(initialDate, periodBeginDelta, periodLength));
     final ChargeDefinition chargeDefinition = new ChargeDefinition();
-    chargeDefinition.setChargeMethod(ChargeDefinition.ChargeMethod.PROPORTIONAL);
+    chargeDefinition.setChargeMethod(ChargeDefinition.ChargeMethod.INTEREST);
     chargeDefinition.setForCycleSizeUnit(ChronoUnit.YEARS);
     chargeDefinition.setIdentifier("blah");
     chargeDefinition.setAccrueAction(Action.APPLY_INTEREST.name());
     chargeDefinition.setChargeAction(Action.ACCEPT_PAYMENT.name());
-    chargeDefinition.setAmount(BigDecimal.valueOf(amount));
+    chargeDefinition.setAmount(BigDecimal.ONE);
     chargeDefinition.setFromAccountDesignator(AccountDesignators.CUSTOMER_LOAN);
     chargeDefinition.setAccrualAccountDesignator(AccountDesignators.INTEREST_ACCRUAL);
     chargeDefinition.setToAccountDesignator(AccountDesignators.INTEREST_INCOME);
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/IndividualLoanServiceTest.java b/service/src/test/java/io/mifos/individuallending/internal/service/IndividualLoanServiceTest.java
index 0cbf933..1bb13b1 100644
--- a/service/src/test/java/io/mifos/individuallending/internal/service/IndividualLoanServiceTest.java
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/IndividualLoanServiceTest.java
@@ -24,8 +24,9 @@
 import io.mifos.individuallending.api.v1.domain.product.ChargeIdentifiers;
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
 import io.mifos.portfolio.api.v1.domain.*;
+import io.mifos.portfolio.service.internal.repository.CaseEntity;
+import io.mifos.portfolio.service.internal.repository.ProductEntity;
 import io.mifos.portfolio.service.internal.service.ChargeDefinitionService;
-import io.mifos.portfolio.service.internal.service.ProductService;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -86,6 +87,7 @@
     private CaseParameters caseParameters;
     private LocalDate initialDisbursementDate;
     private List<ChargeDefinition> chargeDefinitions;
+    private BigDecimal interest;
     private Set<String> expectedChargeIdentifiers = new HashSet<>(Arrays.asList(
         PROCESSING_FEE_ID,
         LOAN_FUNDS_ALLOCATION_ID,
@@ -127,6 +129,11 @@
       return this;
     }
 
+    TestCase interest(final BigDecimal newVal) {
+      this.interest = newVal;
+      return this;
+    }
+
     TestCase expectChargeInstancesForActionDatePair(final Action action,
                                                     final LocalDate forDate,
                                                     final List<ChargeDefinition> chargeDefinitions) {
@@ -134,6 +141,17 @@
       return this;
     }
 
+    DataContextOfAction getDataContextOfAction() {
+
+      final ProductEntity product = new ProductEntity();
+      product.setMinorCurrencyUnitDigits(minorCurrencyUnitDigits);
+      product.setIdentifier(productIdentifier);
+      final CaseEntity customerCase = new CaseEntity();
+      customerCase.setInterest(interest);
+
+      return new DataContextOfAction(product, customerCase, caseParameters, Collections.emptyList());
+    }
+
     @Override
     public String toString() {
       return "TestCase{" +
@@ -153,7 +171,6 @@
 
   private final TestCase testCase;
   private final IndividualLoanService testSubject;
-  private final Product product;
   private final Map<String, List<ChargeDefinition>> chargeDefinitionsByChargeAction;
   private final Map<String, List<ChargeDefinition>> chargeDefinitionsByAccrueAction;
 
@@ -169,7 +186,7 @@
     final ChargeDefinition processingFeeCharge = getFixedSingleChargeDefinition(10.0, Action.OPEN, PROCESSING_FEE_ID, AccountDesignators.PROCESSING_FEE_INCOME);
     final ChargeDefinition loanOriginationFeeCharge = getFixedSingleChargeDefinition(100.0, Action.APPROVE, LOAN_ORIGINATION_FEE_ID, AccountDesignators.ORIGINATION_FEE_INCOME);
     final List<ChargeDefinition> defaultChargesWithFeesReplaced =
-    chargesWithInterestRate(0.01).stream().map(x -> {
+    charges().stream().map(x -> {
       switch (x.getIdentifier()) {
         case PROCESSING_FEE_ID:
           return processingFeeCharge;
@@ -185,6 +202,7 @@
         .caseParameters(caseParameters)
         .initialDisbursementDate(initialDisbursementDate)
         .chargeDefinitions(defaultChargesWithFeesReplaced)
+        .interest(BigDecimal.valueOf(1))
         .expectChargeInstancesForActionDatePair(Action.OPEN, initialDisbursementDate, Collections.singletonList(processingFeeCharge))
         .expectChargeInstancesForActionDatePair(Action.APPROVE, initialDisbursementDate,
             Collections.singletonList(loanOriginationFeeCharge));
@@ -199,13 +217,14 @@
     caseParameters.setPaymentCycle(new PaymentCycle(ChronoUnit.MONTHS, 1, 0, null, null));
     caseParameters.setMaximumBalance(BigDecimal.valueOf(200000));
 
-    final List<ChargeDefinition> charges = chargesWithInterestRate(0.10);
+    final List<ChargeDefinition> charges = charges();
 
     return new TestCase("yearLoanTestCase")
         .minorCurrencyUnitDigits(3)
         .caseParameters(caseParameters)
         .initialDisbursementDate(initialDisbursementDate)
-        .chargeDefinitions(charges);
+        .chargeDefinitions(charges)
+        .interest(BigDecimal.valueOf(10));
   }
 
   private static TestCase chargeDefaultsCase()
@@ -216,24 +235,18 @@
     caseParameters.setPaymentCycle(new PaymentCycle(ChronoUnit.WEEKS, 1, 1, 0, 0));
     caseParameters.setMaximumBalance(BigDecimal.valueOf(2000));
 
-    final List<ChargeDefinition> charges = chargesWithInterestRate(0.05);
+    final List<ChargeDefinition> charges = charges();
 
     return new TestCase("chargeDefaultsCase")
         .minorCurrencyUnitDigits(2)
         .caseParameters(caseParameters)
         .initialDisbursementDate(initialDisbursementDate)
-        .chargeDefinitions(charges);
+        .chargeDefinitions(charges)
+        .interest(BigDecimal.valueOf(5));
   }
 
-  private static List<ChargeDefinition> chargesWithInterestRate(final double interestRate) {
-    final List<ChargeDefinition> defaultLoanCharges = IndividualLendingPatternFactory.defaultIndividualLoanCharges();
-
-    defaultLoanCharges.forEach(x -> {
-      if (x.getIdentifier().equals(ChargeIdentifiers.INTEREST_ID))
-        x.setAmount(BigDecimal.valueOf(interestRate));
-    });
-
-    return defaultLoanCharges;
+  private static List<ChargeDefinition> charges() {
+    return IndividualLendingPatternFactory.defaultIndividualLoanCharges();
   }
 
   private static ChargeDefinition getFixedSingleChargeDefinition(
@@ -258,11 +271,7 @@
   {
     this.testCase = testCase;
 
-    final ProductService productServiceMock = Mockito.mock(ProductService.class);
     final ChargeDefinitionService chargeDefinitionServiceMock = Mockito.mock(ChargeDefinitionService.class);
-    product = new Product();
-    product.setMinorCurrencyUnitDigits(testCase.minorCurrencyUnitDigits);
-    Mockito.doReturn(Optional.of(product)).when(productServiceMock).findByIdentifier(testCase.productIdentifier);
     chargeDefinitionsByChargeAction = testCase.chargeDefinitions.stream()
         .collect(Collectors.groupingBy(ChargeDefinition::getChargeAction,
             Collectors.mapping(x -> x, Collectors.toList())));
@@ -273,21 +282,24 @@
     Mockito.doReturn(chargeDefinitionsByChargeAction).when(chargeDefinitionServiceMock).getChargeDefinitionsMappedByChargeAction(testCase.productIdentifier);
     Mockito.doReturn(chargeDefinitionsByAccrueAction).when(chargeDefinitionServiceMock).getChargeDefinitionsMappedByAccrueAction(testCase.productIdentifier);
 
-    testSubject = new IndividualLoanService(productServiceMock, chargeDefinitionServiceMock);
+    testSubject = new IndividualLoanService(chargeDefinitionServiceMock);
   }
 
   @Test
   public void getPlannedPayments() throws Exception {
-    final PlannedPaymentPage firstPage = testSubject.getPlannedPaymentsPage(testCase.productIdentifier,
-            testCase.caseParameters,
+    final PlannedPaymentPage firstPage = testSubject.getPlannedPaymentsPage(testCase.getDataContextOfAction(),
             0,
             20,
             testCase.initialDisbursementDate);
 
+    Assert.assertFalse(firstPage.getElements().size() == 0);
+
     final List<PlannedPayment> allPlannedPayments =
-            Stream.iterate(0, x -> x + 1).limit(firstPage.getTotalPages())
-            .map(x -> testSubject.getPlannedPaymentsPage(testCase.productIdentifier,
-                    testCase.caseParameters, x, 20, testCase.initialDisbursementDate))
+        Stream.iterate(0, x -> x + 1).limit(firstPage.getTotalPages())
+            .map(x -> testSubject.getPlannedPaymentsPage(testCase.getDataContextOfAction(),
+                x,
+                20,
+                testCase.initialDisbursementDate))
             .flatMap(x -> x.getElements().stream())
             .collect(Collectors.toList());
 
@@ -314,8 +326,8 @@
 
     //All entries should have the correct scale.
     allPlannedPayments.forEach(x -> {
-      x.getCostComponents().forEach(y -> Assert.assertEquals(product.getMinorCurrencyUnitDigits(), y.getAmount().scale()));
-      Assert.assertEquals(product.getMinorCurrencyUnitDigits(), x.getRemainingPrincipal().scale());
+      x.getCostComponents().forEach(y -> Assert.assertEquals(testCase.minorCurrencyUnitDigits, y.getAmount().scale()));
+      Assert.assertEquals(testCase.minorCurrencyUnitDigits, x.getRemainingPrincipal().scale());
       final int uniqueChargeIdentifierCount = x.getCostComponents().stream()
           .map(CostComponent::getChargeIdentifier)
           .collect(Collectors.toSet())
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/PeriodChargeCalculatorTest.java b/service/src/test/java/io/mifos/individuallending/internal/service/PeriodChargeCalculatorTest.java
index 6b3c2ff..0961727 100644
--- a/service/src/test/java/io/mifos/individuallending/internal/service/PeriodChargeCalculatorTest.java
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/PeriodChargeCalculatorTest.java
@@ -38,6 +38,7 @@
     List<ScheduledCharge> scheduledCharges;
     int precision;
     Map<Period, BigDecimal> expectedPeriodRates;
+    private BigDecimal interest;
 
     private TestCase(final String description) {
       this.description = description;
@@ -64,6 +65,11 @@
               "description='" + description + '\'' +
               '}';
     }
+
+    TestCase interest(BigDecimal newVal) {
+      this.interest = newVal;
+      return this;
+    }
   }
 
   @Parameterized.Parameters
@@ -79,8 +85,8 @@
   {
     final LocalDate initialDate = LocalDate.now();
     final List<ScheduledCharge> scheduledCharges = new ArrayList<>();
-    scheduledCharges.add(scheduledInterestBookingCharge(0.01, initialDate, 0, 0, 1));
-    scheduledCharges.add(scheduledInterestBookingCharge(0.01, initialDate, 1, 1, 1));
+    scheduledCharges.add(scheduledInterestBookingCharge(initialDate, 0, 0, 1));
+    scheduledCharges.add(scheduledInterestBookingCharge(initialDate, 1, 1, 1));
 
     final BigDecimal dailyInterestRate = BigDecimal.valueOf(0.01)
         .divide(BigDecimal.valueOf(365.2425), 20, BigDecimal.ROUND_HALF_EVEN);
@@ -90,6 +96,7 @@
     expectedPeriodRates.put(getPeriod(initialDate, 1, 1), dailyInterestRate);
 
     return new TestCase("simpleCase")
+        .interest(BigDecimal.valueOf(0.01))
         .scheduledCharges(scheduledCharges)
         .precision(20)
         .expectedPeriodRates(expectedPeriodRates);
@@ -99,8 +106,8 @@
   {
     final LocalDate initialDate = LocalDate.now();
     final List<ScheduledCharge> scheduledCharges = new ArrayList<>();
-    scheduledCharges.add(scheduledInterestBookingCharge(0.10, initialDate, 2, 0, 3));
-    scheduledCharges.add(scheduledInterestBookingCharge(0.10, initialDate, 4, 2, 2));
+    scheduledCharges.add(scheduledInterestBookingCharge(initialDate, 2, 0, 3));
+    scheduledCharges.add(scheduledInterestBookingCharge(initialDate, 4, 2, 2));
 
     final BigDecimal dailyInterestRate = BigDecimal.valueOf(0.10)
         .divide(BigDecimal.valueOf(365.2425), 20, BigDecimal.ROUND_HALF_EVEN);
@@ -110,6 +117,7 @@
     expectedPeriodRates.put(getPeriod(initialDate, 2, 2), PeriodChargeCalculator.createCompoundedRate(dailyInterestRate, 2, 20));
 
     return new TestCase("bitOfCompoundingCase")
+        .interest(BigDecimal.valueOf(0.10))
         .scheduledCharges(scheduledCharges)
         .precision(20)
         .expectedPeriodRates(expectedPeriodRates);
@@ -119,14 +127,15 @@
   {
     final LocalDate initialDate = LocalDate.now();
     final List<ScheduledCharge> scheduledCharges = new ArrayList<>();
-    scheduledCharges.add(scheduledInterestBookingCharge(0.00, initialDate, 2, 0, 3));
-    scheduledCharges.add(scheduledInterestBookingCharge(0.00, initialDate, 4, 2, 2));
+    scheduledCharges.add(scheduledInterestBookingCharge(initialDate, 2, 0, 3));
+    scheduledCharges.add(scheduledInterestBookingCharge(initialDate, 4, 2, 2));
 
     final Map<Period, BigDecimal> expectedPeriodRates = new HashMap<>();
     expectedPeriodRates.put(getPeriod(initialDate, 0, 3), BigDecimal.ZERO.setScale(20, BigDecimal.ROUND_UNNECESSARY));
     expectedPeriodRates.put(getPeriod(initialDate, 2, 2), BigDecimal.ZERO.setScale(20, BigDecimal.ROUND_UNNECESSARY));
 
     return new TestCase("zeroInterestPerPeriod")
+        .interest(BigDecimal.valueOf(0.00))
         .scheduledCharges(scheduledCharges)
         .precision(20)
         .expectedPeriodRates(expectedPeriodRates);
@@ -141,7 +150,7 @@
   @Test
   public void getPeriodAccrualRatesTest()
   {
-    final Map<Period, BigDecimal> periodRates = PeriodChargeCalculator.getPeriodAccrualInterestRate(testCase.scheduledCharges, testCase.precision);
+    final Map<Period, BigDecimal> periodRates = PeriodChargeCalculator.getPeriodAccrualInterestRate(testCase.interest, testCase.scheduledCharges, testCase.precision);
     Assert.assertEquals(testCase.expectedPeriodRates, periodRates);
   }
 }
diff --git a/shared.gradle b/shared.gradle
index 88fd123..410d57d 100644
--- a/shared.gradle
+++ b/shared.gradle
@@ -12,6 +12,7 @@
         frameworkasync      : '0.1.0-BUILD-SNAPSHOT',
         frameworkaccounting : '0.1.0-BUILD-SNAPSHOT',
         mifosrhythm         : '0.1.0-BUILD-SNAPSHOT',
+        mifoscustomer       : '0.1.0-BUILD-SNAPSHOT',
         validator    : '5.3.0.Final',
         javamoneylib : '0.9-SNAPSHOT'
 ]
@@ -70,5 +71,6 @@
         uxf = 'XML_STYLE'
     }
     ext.year = Calendar.getInstance().get(Calendar.YEAR)
-    ext.name = 'The Mifos Initiative'
+    ext.name = 'Kuelap, Inc'
+    skipExistingHeaders true
 }