Using range associations for charges to determine which charges to apply when.
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 f533a12..dc3cdfe 100644
--- a/component-test/src/main/java/io/mifos/portfolio/Fixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
@@ -52,7 +52,7 @@
     product.setName("Agricultural Loan");
     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.setBalanceRange(new BalanceRange(fixScale(BigDecimal.ZERO), fixScale(new BigDecimal(10_000))));
     product.setInterestRange(new InterestRange(BigDecimal.valueOf(3_00, 2), BigDecimal.valueOf(12_00, 2)));
     product.setInterestBasis(InterestBasis.CURRENT_BALANCE);
 
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 a49d578..c4a9e5f 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
@@ -54,10 +54,10 @@
  * @author Myrle Krantz
  */
 public class TestAccountingInteractionInLoanWorkflow extends AbstractPortfolioTest {
-  private static final BigDecimal PROCESSING_FEE_AMOUNT = BigDecimal.valueOf(10_0000, MINOR_CURRENCY_UNIT_DIGITS);
-  private static final BigDecimal LOAN_ORIGINATION_FEE_AMOUNT = BigDecimal.valueOf(100_0000, MINOR_CURRENCY_UNIT_DIGITS);
-  private static final BigDecimal DISBURSEMENT_FEE_LOWER_RANGE_AMOUNT = BigDecimal.valueOf(10_0000, MINOR_CURRENCY_UNIT_DIGITS);
-  private static final BigDecimal DISBURSEMENT_FEE_UPPER_RANGE_AMOUNT = BigDecimal.valueOf(1_0000, MINOR_CURRENCY_UNIT_DIGITS);
+  private static final BigDecimal PROCESSING_FEE_AMOUNT = BigDecimal.valueOf(100_00, MINOR_CURRENCY_UNIT_DIGITS);
+  private static final BigDecimal LOAN_ORIGINATION_FEE_AMOUNT = BigDecimal.valueOf(100_00, MINOR_CURRENCY_UNIT_DIGITS);
+  private static final BigDecimal DISBURSEMENT_FEE_LOWER_RANGE_AMOUNT = BigDecimal.valueOf(10_00, MINOR_CURRENCY_UNIT_DIGITS);
+  private static final BigDecimal DISBURSEMENT_FEE_UPPER_RANGE_AMOUNT = BigDecimal.valueOf(1_00, MINOR_CURRENCY_UNIT_DIGITS);
   private static final String DISBURSEMENT_RANGES = "disbursement_ranges";
   private static final String DISBURSEMENT_LOWER_RANGE = "smaller";
   private static final String DISBURSEMENT_UPPER_RANGE = "larger";
@@ -95,7 +95,26 @@
     step2CreateCase();
     step3OpenCase();
     step4ApproveCase();
-    step5Disburse(BigDecimal.valueOf(2000L).setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN));
+    step5Disburse(
+        BigDecimal.valueOf(2_000_00, MINOR_CURRENCY_UNIT_DIGITS),
+        UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(20_00, MINOR_CURRENCY_UNIT_DIGITS));
+    step6CalculateInterestAccrual();
+    step7PaybackPartialAmount(expectedCurrentBalance);
+    step8Close();
+  }
+
+  @Test
+  public void workflowWithTwoUnequalDisbursals() throws InterruptedException {
+    step1CreateProduct();
+    step2CreateCase();
+    step3OpenCase();
+    step4ApproveCase();
+    step5Disburse(
+        BigDecimal.valueOf(500_00, MINOR_CURRENCY_UNIT_DIGITS),
+        ChargeIdentifiers.DISBURSEMENT_FEE_ID, BigDecimal.valueOf(10_00, MINOR_CURRENCY_UNIT_DIGITS));
+    step5Disburse(
+        BigDecimal.valueOf(1_500_00, MINOR_CURRENCY_UNIT_DIGITS),
+        UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(15_00, MINOR_CURRENCY_UNIT_DIGITS));
     step6CalculateInterestAccrual();
     step7PaybackPartialAmount(expectedCurrentBalance);
     step8Close();
@@ -107,7 +126,9 @@
     step2CreateCase();
     step3OpenCase();
     step4ApproveCase();
-    step5Disburse(BigDecimal.valueOf(2000L).setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN));
+    step5Disburse(
+        BigDecimal.valueOf(2_000_00, MINOR_CURRENCY_UNIT_DIGITS),
+        UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(20_00, MINOR_CURRENCY_UNIT_DIGITS));
     step6CalculateInterestAccrual();
     final BigDecimal repayment1 = expectedCurrentBalance.divide(BigDecimal.valueOf(2), BigDecimal.ROUND_HALF_EVEN);
     step7PaybackPartialAmount(repayment1.setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN));
@@ -122,7 +143,8 @@
     step3OpenCase();
     step4ApproveCase();
     try {
-      step5Disburse(BigDecimal.valueOf(-2).setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN));
+      step5Disburse(BigDecimal.valueOf(-2).setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN),
+          UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.ZERO.setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN));
       Assert.fail("Expected an IllegalArgumentException.");
     }
     catch (IllegalArgumentException ignored) { }
@@ -147,7 +169,7 @@
         = portfolioManager.getChargeDefinition(product.getIdentifier(), ChargeIdentifiers.DISBURSEMENT_FEE_ID);
     lowerRangeDisbursementFeeChargeDefinition.setChargeMethod(ChargeDefinition.ChargeMethod.FIXED);
     lowerRangeDisbursementFeeChargeDefinition.setAmount(DISBURSEMENT_FEE_LOWER_RANGE_AMOUNT);
-    lowerRangeDisbursementFeeChargeDefinition.setProportionalTo(ChargeProportionalDesignator.PRINCIPAL_ADJUSTMENT_DESIGNATOR.name());
+    lowerRangeDisbursementFeeChargeDefinition.setProportionalTo(ChargeProportionalDesignator.PRINCIPAL_ADJUSTMENT_DESIGNATOR.getValue());
     lowerRangeDisbursementFeeChargeDefinition.setForSegmentSet(DISBURSEMENT_RANGES);
     lowerRangeDisbursementFeeChargeDefinition.setFromSegment(DISBURSEMENT_LOWER_RANGE);
     lowerRangeDisbursementFeeChargeDefinition.setToSegment(DISBURSEMENT_LOWER_RANGE);
@@ -167,7 +189,7 @@
     upperRangeDisbursementFeeChargeDefinition.setChargeAction(lowerRangeDisbursementFeeChargeDefinition.getChargeAction());
     upperRangeDisbursementFeeChargeDefinition.setChargeMethod(ChargeDefinition.ChargeMethod.PROPORTIONAL);
     upperRangeDisbursementFeeChargeDefinition.setAmount(DISBURSEMENT_FEE_UPPER_RANGE_AMOUNT);
-    upperRangeDisbursementFeeChargeDefinition.setProportionalTo(ChargeProportionalDesignator.PRINCIPAL_ADJUSTMENT_DESIGNATOR.name());
+    upperRangeDisbursementFeeChargeDefinition.setProportionalTo(ChargeProportionalDesignator.PRINCIPAL_ADJUSTMENT_DESIGNATOR.getValue());
     upperRangeDisbursementFeeChargeDefinition.setForSegmentSet(DISBURSEMENT_RANGES);
     upperRangeDisbursementFeeChargeDefinition.setFromSegment(DISBURSEMENT_UPPER_RANGE);
     upperRangeDisbursementFeeChargeDefinition.setToSegment(DISBURSEMENT_UPPER_RANGE);
@@ -278,15 +300,17 @@
   }
 
   //Approve the case, accept a loan origination fee, and prepare to disburse the loan by earmarking the funds.
-  private void step5Disburse(final BigDecimal amount) throws InterruptedException {
+  private void step5Disburse(
+      final BigDecimal amount,
+      final String whichDisbursementFee,
+      final BigDecimal disbursementFeeAmount) throws InterruptedException {
     logger.info("step5Disburse");
     checkCostComponentForActionCorrect(
         product.getIdentifier(),
         customerCase.getIdentifier(),
         Action.DISBURSE,
         Collections.singleton(AccountDesignators.ENTRY),
-        amount, new CostComponent(ChargeIdentifiers.DISBURSEMENT_FEE_ID, DISBURSEMENT_FEE_LOWER_RANGE_AMOUNT),
-        new CostComponent(UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.ZERO.setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN)),
+        amount, new CostComponent(whichDisbursementFee, disbursementFeeAmount),
         new CostComponent(ChargeIdentifiers.DISBURSE_PAYMENT_ID, amount));
     checkStateTransfer(
         product.getIdentifier(),
@@ -303,12 +327,12 @@
     final Set<Debtor> debtors = new HashSet<>();
     debtors.add(new Debtor(pendingDisbursalAccountIdentifier, amount.toPlainString()));
     debtors.add(new Debtor(AccountingFixture.LOANS_PAYABLE_ACCOUNT_IDENTIFIER, amount.toPlainString()));
-    debtors.add(new Debtor(AccountingFixture.TELLER_ONE_ACCOUNT_IDENTIFIER, DISBURSEMENT_FEE_LOWER_RANGE_AMOUNT.toPlainString()));
+    debtors.add(new Debtor(AccountingFixture.TELLER_ONE_ACCOUNT_IDENTIFIER, disbursementFeeAmount.toPlainString()));
 
     final Set<Creditor> creditors = new HashSet<>();
     creditors.add(new Creditor(customerLoanAccountIdentifier, amount.toPlainString()));
     creditors.add(new Creditor(AccountingFixture.TELLER_ONE_ACCOUNT_IDENTIFIER, amount.toPlainString()));
-    creditors.add(new Creditor(AccountingFixture.DISBURSEMENT_FEE_INCOME_ACCOUNT_IDENTIFIER, DISBURSEMENT_FEE_LOWER_RANGE_AMOUNT.toPlainString()));
+    creditors.add(new Creditor(AccountingFixture.DISBURSEMENT_FEE_INCOME_ACCOUNT_IDENTIFIER, disbursementFeeAmount.toPlainString()));
     AccountingFixture.verifyTransfer(ledgerManager, debtors, creditors, product.getIdentifier(), customerCase.getIdentifier(), Action.DISBURSE);
 
     expectedCurrentBalance = expectedCurrentBalance.add(amount);
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/ChargeRange.java b/service/src/main/java/io/mifos/individuallending/internal/service/ChargeRange.java
new file mode 100644
index 0000000..cd293d5
--- /dev/null
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/ChargeRange.java
@@ -0,0 +1,64 @@
+/*
+ * 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 java.math.BigDecimal;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * @author Myrle Krantz
+ */
+class ChargeRange {
+  final private BigDecimal from;
+  @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+  final private Optional<BigDecimal> to;
+
+  ChargeRange(
+      final BigDecimal from,
+      @SuppressWarnings("OptionalUsedAsFieldOrParameterType") final Optional<BigDecimal> to) {
+    this.from = from;
+    this.to = to;
+  }
+
+  boolean amountIsWithinRange(BigDecimal amountProportionalTo) {
+    return to.map(bigDecimal -> from.compareTo(amountProportionalTo) <= 0 &&
+        bigDecimal.compareTo(amountProportionalTo) > 0)
+        .orElseGet(() -> from.compareTo(amountProportionalTo) <= 0);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ChargeRange that = (ChargeRange) o;
+    return Objects.equals(from, that.from) &&
+        Objects.equals(to, that.to);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(from, to);
+  }
+
+  @Override
+  public String toString() {
+    return "ChargeRange{" +
+        "from=" + from +
+        ", to=" + to +
+        '}';
+  }
+}
\ No newline at end of file
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 318db3b..c3ba820 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
@@ -47,14 +47,14 @@
   private static final int EXTRA_PRECISION = 4;
   private static final int RUNNING_CALCULATION_PRECISION = 8;
 
-  private final IndividualLoanService individualLoanService;
+  private final ScheduledChargesService scheduledChargesService;
   private final AccountingAdapter accountingAdapter;
 
   @Autowired
   public CostComponentService(
-          final IndividualLoanService individualLoanService,
-          final AccountingAdapter accountingAdapter) {
-    this.individualLoanService = individualLoanService;
+      final ScheduledChargesService scheduledChargesService,
+      final AccountingAdapter accountingAdapter) {
+    this.scheduledChargesService = scheduledChargesService;
     this.accountingAdapter = accountingAdapter;
   }
 
@@ -93,7 +93,7 @@
     final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
     final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
     final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.OPEN, today()));
-    final List<ScheduledCharge> scheduledCharges = individualLoanService.getScheduledCharges(
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier, scheduledActions);
 
     return getCostComponentsForScheduledCharges(
@@ -112,7 +112,7 @@
     final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
     final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
     final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.DENY, today()));
-    final List<ScheduledCharge> scheduledCharges = individualLoanService.getScheduledCharges(
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier, scheduledActions);
 
     return getCostComponentsForScheduledCharges(
@@ -132,7 +132,7 @@
     final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
     final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
     final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.APPROVE, today()));
-    final List<ScheduledCharge> scheduledCharges = individualLoanService.getScheduledCharges(
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier, scheduledActions);
 
     return getCostComponentsForScheduledCharges(
@@ -173,7 +173,7 @@
     else
       disbursalSize = requestedDisbursalSize.negate();
 
-    final List<ScheduledCharge> scheduledCharges = individualLoanService.getScheduledCharges(
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier, scheduledActions);
 
 
@@ -219,7 +219,7 @@
     final LocalDate today = today();
     final ScheduledAction interestAction = new ScheduledAction(Action.APPLY_INTEREST, today, new Period(1, today));
 
-    final List<ScheduledCharge> scheduledCharges = individualLoanService.getScheduledCharges(
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier,
         Collections.singletonList(interestAction));
 
@@ -271,7 +271,7 @@
       final List<ScheduledAction> hypotheticalScheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(
           today(),
           caseParameters);
-      final List<ScheduledCharge> hypotheticalScheduledCharges = individualLoanService.getScheduledCharges(
+      final List<ScheduledCharge> hypotheticalScheduledCharges = scheduledChargesService.getScheduledCharges(
           productIdentifier,
           hypotheticalScheduledActions);
       loanPaymentSize = getLoanPaymentSize(
@@ -281,7 +281,7 @@
           hypotheticalScheduledCharges);
     }
 
-    final List<ScheduledCharge> scheduledChargesForThisAction = individualLoanService.getScheduledCharges(
+    final List<ScheduledCharge> scheduledChargesForThisAction = scheduledChargesService.getScheduledCharges(
         productIdentifier,
         Collections.singletonList(scheduledAction));
 
@@ -307,12 +307,12 @@
         true);
   }
 
-  public static boolean isAccruedChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
+  private static boolean isAccruedChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
     return chargeDefinition.getAccrueAction() != null &&
         chargeDefinition.getChargeAction().equals(action.name());
   }
 
-  public static boolean isAccrualChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
+  private static boolean isAccrualChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
     return chargeDefinition.getAccrueAction() != null &&
         chargeDefinition.getAccrueAction().equals(action.name());
   }
@@ -367,7 +367,7 @@
     final LocalDate today = today();
     final ScheduledAction closeAction = new ScheduledAction(Action.CLOSE, today, new Period(1, today));
 
-    final List<ScheduledCharge> scheduledCharges = individualLoanService.getScheduledCharges(
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier,
         Collections.singletonList(closeAction));
 
@@ -440,6 +440,9 @@
             entryAccountAdjustment,
             balanceAdjustments);
         //TODO: getAmountProportionalTo is programmed under the assumption of non-accrual accounting.
+        if (scheduledCharge.getChargeRange().map(x ->
+            !x.amountIsWithinRange(amountProportionalTo)).orElse(false))
+          continue;
 
         final CostComponent costComponent = costComponentMap
             .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
@@ -468,27 +471,37 @@
       final BigDecimal runningBalance,
       final BigDecimal loanPaymentSize,
       final Map<String, BigDecimal> balanceAdjustments) {
-    final Optional<ChargeProportionalDesignator> optionalChargeProportionalTo = proportionalToDesignator(scheduledCharge);
-    return optionalChargeProportionalTo.map(chargeProportionalTo -> {
-      switch (chargeProportionalTo) {
-        case NOT_PROPORTIONAL:
-          return BigDecimal.ZERO;
-        case MAXIMUM_BALANCE_DESIGNATOR:
-          return maximumBalance;
-        case RUNNING_BALANCE_DESIGNATOR:
-          return runningBalance.subtract(balanceAdjustments.getOrDefault(AccountDesignators.CUSTOMER_LOAN, BigDecimal.ZERO));
-        case REPAYMENT_DESIGNATOR:
-          return loanPaymentSize;
-        case PRINCIPAL_ADJUSTMENT_DESIGNATOR: {
-          final BigDecimal newRunningBalance
-              = runningBalance.subtract(balanceAdjustments.getOrDefault(AccountDesignators.CUSTOMER_LOAN, BigDecimal.ZERO));
-          final BigDecimal newLoanPaymentSize = loanPaymentSize.min(newRunningBalance);
-          return newLoanPaymentSize.add(balanceAdjustments.getOrDefault(AccountDesignators.CUSTOMER_LOAN, BigDecimal.ZERO)).abs();
-        }
-        default:
-          return BigDecimal.ZERO;
+    final Optional<ChargeProportionalDesignator> optionalChargeProportionalTo
+        = ChargeProportionalDesignator.fromString(scheduledCharge.getChargeDefinition().getProportionalTo());
+    return optionalChargeProportionalTo.map(chargeProportionalTo ->
+        getAmountProportionalTo(chargeProportionalTo, maximumBalance, runningBalance, loanPaymentSize, balanceAdjustments))
+        .orElse(BigDecimal.ZERO);
+  }
+
+  static BigDecimal getAmountProportionalTo(
+      final ChargeProportionalDesignator chargeProportionalTo,
+      final BigDecimal maximumBalance,
+      final BigDecimal runningBalance,
+      final BigDecimal loanPaymentSize,
+      final Map<String, BigDecimal> balanceAdjustments) {
+    switch (chargeProportionalTo) {
+      case NOT_PROPORTIONAL:
+        return BigDecimal.ZERO;
+      case MAXIMUM_BALANCE_DESIGNATOR:
+        return maximumBalance;
+      case RUNNING_BALANCE_DESIGNATOR:
+        return runningBalance.subtract(balanceAdjustments.getOrDefault(AccountDesignators.CUSTOMER_LOAN, BigDecimal.ZERO));
+      case REPAYMENT_DESIGNATOR:
+        return loanPaymentSize;
+      case PRINCIPAL_ADJUSTMENT_DESIGNATOR: {
+        final BigDecimal newRunningBalance
+            = runningBalance.subtract(balanceAdjustments.getOrDefault(AccountDesignators.CUSTOMER_LOAN, BigDecimal.ZERO));
+        final BigDecimal newLoanPaymentSize = loanPaymentSize.min(newRunningBalance);
+        return newLoanPaymentSize.add(balanceAdjustments.getOrDefault(AccountDesignators.CUSTOMER_LOAN, BigDecimal.ZERO)).abs();
       }
-    }).orElse(BigDecimal.ZERO);
+      default:
+        return BigDecimal.ZERO;
+    }
 //TODO: correctly implement charges which are proportional to other charges.
   }
 
@@ -499,14 +512,6 @@
     return ret;
   }
 
-  private static Optional<ChargeProportionalDesignator> proportionalToDesignator(final ScheduledCharge scheduledCharge) {
-    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 BigDecimal interest)
   {
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 6320deb..e836004 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
@@ -18,9 +18,6 @@
 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.service.internal.service.ChargeDefinitionService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -30,18 +27,17 @@
 import java.util.*;
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 /**
  * @author Myrle Krantz
  */
 @Service
 public class IndividualLoanService {
-  private final ChargeDefinitionService chargeDefinitionService;
+  private final ScheduledChargesService scheduledChargesService;
 
   @Autowired
-  public IndividualLoanService(final ChargeDefinitionService chargeDefinitionService) {
-    this.chargeDefinitionService = chargeDefinitionService;
+  public IndividualLoanService(final ScheduledChargesService scheduledChargesService) {
+    this.scheduledChargesService = scheduledChargesService;
   }
 
   public PlannedPaymentPage getPlannedPaymentsPage(
@@ -53,7 +49,7 @@
 
     final List<ScheduledAction> scheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(initialDisbursalDate, dataContextOfAction.getCaseParameters());
 
-    final List<ScheduledCharge> scheduledCharges = getScheduledCharges(dataContextOfAction.getProduct().getIdentifier(), scheduledActions);
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(dataContextOfAction.getProduct().getIdentifier(), scheduledActions);
 
     final BigDecimal loanPaymentSize = CostComponentService.getLoanPaymentSize(
         dataContextOfAction.getCaseParameters().getMaximumBalance(),
@@ -98,21 +94,6 @@
     return new ChargeName(scheduledCharge.getChargeDefinition().getIdentifier(), scheduledCharge.getChargeDefinition().getName());
   }
 
-  List<ScheduledCharge> getScheduledCharges(
-      final String productIdentifier,
-      final @Nonnull List<ScheduledAction> scheduledActions) {
-    final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction
-            = chargeDefinitionService.getChargeDefinitionsMappedByChargeAction(productIdentifier);
-
-    final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction
-            = chargeDefinitionService.getChargeDefinitionsMappedByAccrueAction(productIdentifier);
-
-    return getScheduledCharges(
-            scheduledActions,
-            chargeDefinitionsMappedByChargeAction,
-            chargeDefinitionsMappedByAccrueAction);
-  }
-
   static private List<PlannedPayment> getPlannedPaymentsElements(
       final BigDecimal initialBalance,
       final int minorCurrencyUnitDigits,
@@ -124,7 +105,7 @@
         .collect(Collectors.groupingBy(IndividualLoanService::getPeriodFromScheduledCharge,
             Collectors.mapping(x -> x,
                 Collector.of(
-                    () -> new TreeSet<>(new ScheduledChargeComparator()),
+                    () -> new TreeSet<>(new ScheduledChargesService.ScheduledChargeComparator()),
                     SortedSet::add,
                     (left, right) -> { left.addAll(right); return left; }))));
 
@@ -179,72 +160,4 @@
     else
       return scheduledAction.repaymentPeriod;
   }
-
-  static List<ScheduledCharge> getScheduledCharges(final List<ScheduledAction> scheduledActions,
-                                                    final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction,
-                                                    final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction) {
-    return scheduledActions.stream()
-        .flatMap(scheduledAction ->
-            getChargeDefinitionStream(
-                chargeDefinitionsMappedByChargeAction,
-                chargeDefinitionsMappedByAccrueAction,
-                scheduledAction)
-                .map(chargeDefinition -> new ScheduledCharge(scheduledAction, chargeDefinition)))
-        .collect(Collectors.toList());
-  }
-
-  private static Stream<ChargeDefinition> getChargeDefinitionStream(
-          final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction,
-          final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction,
-          final ScheduledAction scheduledAction) {
-    final List<ChargeDefinition> chargeMappingList = chargeDefinitionsMappedByChargeAction
-        .get(scheduledAction.action.name());
-    Stream<ChargeDefinition> chargeMapping = chargeMappingList == null ? Stream.empty() : chargeMappingList.stream();
-    if (chargeMapping == null)
-      chargeMapping = Stream.empty();
-
-    final List<ChargeDefinition> accrueMappingList = chargeDefinitionsMappedByAccrueAction
-        .get(scheduledAction.action.name());
-    Stream<ChargeDefinition> accrueMapping = accrueMappingList == null ? Stream.empty() : accrueMappingList.stream();
-    if (accrueMapping == null)
-      accrueMapping = Stream.empty();
-
-    return Stream.concat(
-        accrueMapping.sorted(IndividualLoanService::proportionalityApplicationOrder),
-        chargeMapping.sorted(IndividualLoanService::proportionalityApplicationOrder));
-  }
-
-  private static class ScheduledChargeComparator implements Comparator<ScheduledCharge>
-  {
-    @Override
-    public int compare(ScheduledCharge o1, ScheduledCharge o2) {
-      int ret = o1.getScheduledAction().when.compareTo(o2.getScheduledAction().when);
-      if (ret == 0)
-        ret = o1.getScheduledAction().action.compareTo(o2.getScheduledAction().action);
-      if (ret == 0)
-        ret = proportionalityApplicationOrder(o1.getChargeDefinition(), o2.getChargeDefinition());
-      if (ret == 0)
-        return o1.getChargeDefinition().getIdentifier().compareTo(o2.getChargeDefinition().getIdentifier());
-      else
-        return ret;
-    }
-  }
-
-  private static int proportionalityApplicationOrder(final ChargeDefinition o1, final ChargeDefinition o2) {
-    final Optional<ChargeProportionalDesignator> aProportionalToDesignator
-        = ChargeProportionalDesignator.fromString(o1.getProportionalTo());
-    final Optional<ChargeProportionalDesignator> bProportionalToDesignator
-        = ChargeProportionalDesignator.fromString(o2.getProportionalTo());
-
-    if (aProportionalToDesignator.isPresent() && bProportionalToDesignator.isPresent())
-      return Integer.compare(
-          aProportionalToDesignator.get().getOrderOfApplication(),
-          bProportionalToDesignator.get().getOrderOfApplication());
-    else if (aProportionalToDesignator.isPresent())
-      return 1;
-    else if (bProportionalToDesignator.isPresent())
-      return -1;
-    else
-      return 0;
-  }
 }
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/ScheduledCharge.java b/service/src/main/java/io/mifos/individuallending/internal/service/ScheduledCharge.java
index 60fb56c..7b98317 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/ScheduledCharge.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/ScheduledCharge.java
@@ -18,6 +18,7 @@
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 
 import javax.annotation.Nonnull;
+import java.util.Optional;
 
 /**
  * @author Myrle Krantz
@@ -25,10 +26,16 @@
 public class ScheduledCharge {
   private final ScheduledAction scheduledAction;
   private final ChargeDefinition chargeDefinition;
+  @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+  private final Optional<ChargeRange> chargeRange;
 
-  ScheduledCharge(@Nonnull final ScheduledAction scheduledAction, @Nonnull final ChargeDefinition chargeDefinition) {
+  ScheduledCharge(
+      @Nonnull final ScheduledAction scheduledAction,
+      @Nonnull final ChargeDefinition chargeDefinition,
+      @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Nonnull final Optional<ChargeRange> chargeRange) {
     this.scheduledAction = scheduledAction;
     this.chargeDefinition = chargeDefinition;
+    this.chargeRange = chargeRange;
   }
 
   ScheduledAction getScheduledAction() {
@@ -39,11 +46,16 @@
     return chargeDefinition;
   }
 
+  Optional<ChargeRange> getChargeRange() {
+    return chargeRange;
+  }
+
   @Override
   public String toString() {
     return "ScheduledCharge{" +
-            "scheduledAction=" + scheduledAction +
-            ", chargeDefinition=" + chargeDefinition +
-            '}';
+        "scheduledAction=" + scheduledAction +
+        ", chargeDefinition=" + chargeDefinition +
+        ", chargeRange=" + chargeRange +
+        '}';
   }
 }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/ScheduledChargesService.java b/service/src/main/java/io/mifos/individuallending/internal/service/ScheduledChargesService.java
new file mode 100644
index 0000000..fcbfc6c
--- /dev/null
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/ScheduledChargesService.java
@@ -0,0 +1,191 @@
+/*
+ * 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.individuallending.api.v1.domain.product.ChargeProportionalDesignator;
+import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentEntity;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentRepository;
+import io.mifos.portfolio.service.internal.service.ChargeDefinitionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Nonnull;
+import java.math.BigDecimal;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author Myrle Krantz
+ */
+@Service
+public class ScheduledChargesService {
+  private final ChargeDefinitionService chargeDefinitionService;
+  private final BalanceSegmentRepository balanceSegmentRepository;
+
+  @Autowired
+  public ScheduledChargesService(
+      final ChargeDefinitionService chargeDefinitionService,
+      final BalanceSegmentRepository balanceSegmentRepository) {
+    this.chargeDefinitionService = chargeDefinitionService;
+    this.balanceSegmentRepository = balanceSegmentRepository;
+  }
+
+  List<ScheduledCharge> getScheduledCharges(
+      final String productIdentifier,
+      final @Nonnull List<ScheduledAction> scheduledActions) {
+    final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction
+        = chargeDefinitionService.getChargeDefinitionsMappedByChargeAction(productIdentifier);
+
+    final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction
+        = chargeDefinitionService.getChargeDefinitionsMappedByAccrueAction(productIdentifier);
+
+    return getScheduledCharges(
+        productIdentifier,
+        scheduledActions,
+        chargeDefinitionsMappedByChargeAction,
+        chargeDefinitionsMappedByAccrueAction);
+  }
+
+  private List<ScheduledCharge> getScheduledCharges(
+      final String productIdentifier,
+      final List<ScheduledAction> scheduledActions,
+      final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction,
+      final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction) {
+    return scheduledActions.stream()
+        .flatMap(scheduledAction ->
+            getChargeDefinitionStream(
+                chargeDefinitionsMappedByChargeAction,
+                chargeDefinitionsMappedByAccrueAction,
+                scheduledAction)
+                .map(chargeDefinition -> new ScheduledCharge(
+                    scheduledAction,
+                    chargeDefinition,
+                    findChargeRange(productIdentifier, chargeDefinition))))
+        .collect(Collectors.toList());
+  }
+
+  @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+  private static class Segment {
+    final String identifier;
+    final BigDecimal lowerBound;
+    final Optional<BigDecimal> upperBound;
+
+    private Segment(final String segmentIdentifier,
+            final BigDecimal lowerBound,
+            final Optional<BigDecimal> upperBound) {
+      this.identifier = segmentIdentifier;
+      this.lowerBound = lowerBound;
+      this.upperBound = upperBound;
+    }
+
+    BigDecimal getLowerBound() {
+      return lowerBound;
+    }
+
+    Optional<BigDecimal> getUpperBound() {
+      return upperBound;
+    }
+  }
+
+  Optional<ChargeRange> findChargeRange(final String productIdentifier, final ChargeDefinition chargeDefinition) {
+    if ((chargeDefinition.getForSegmentSet() == null) ||
+        (chargeDefinition.getFromSegment() == null) ||
+        (chargeDefinition.getToSegment() == null))
+      return Optional.empty();
+
+    final List<BalanceSegmentEntity> segmentSet = balanceSegmentRepository.findByProductIdentifierAndSegmentSetIdentifier(productIdentifier, chargeDefinition.getForSegmentSet())
+        .sorted(Comparator.comparing(BalanceSegmentEntity::getLowerBound))
+        .collect(Collectors.toList());
+
+    final Map<String, Segment> segments = Stream.iterate(0, i -> i + 1).limit(segmentSet.size())
+        .map(i -> new Segment(
+            segmentSet.get(i).getSegmentIdentifier(),
+            segmentSet.get(i).getLowerBound(),
+            Optional.ofNullable(i + 1 < segmentSet.size() ?
+                segmentSet.get(i + 1).getLowerBound() :
+                null)
+        ))
+        .collect(Collectors.toMap(x -> x.identifier, x -> x));
+
+
+    final Optional<Segment> fromSegment = Optional.ofNullable(segments.get(chargeDefinition.getFromSegment()));
+    final Optional<Segment> toSegment = Optional.ofNullable(segments.get(chargeDefinition.getToSegment()));
+    if (!fromSegment.isPresent() || !toSegment.isPresent())
+      return Optional.empty();
+
+    return Optional.of(new ChargeRange(fromSegment.get().getLowerBound(), toSegment.get().getUpperBound()));
+  }
+
+  private static Stream<ChargeDefinition> getChargeDefinitionStream(
+      final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction,
+      final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction,
+      final ScheduledAction scheduledAction) {
+    final List<ChargeDefinition> chargeMappingList = chargeDefinitionsMappedByChargeAction
+        .get(scheduledAction.action.name());
+    Stream<ChargeDefinition> chargeMapping = chargeMappingList == null ? Stream.empty() : chargeMappingList.stream();
+    if (chargeMapping == null)
+      chargeMapping = Stream.empty();
+
+    final List<ChargeDefinition> accrueMappingList = chargeDefinitionsMappedByAccrueAction
+        .get(scheduledAction.action.name());
+    Stream<ChargeDefinition> accrueMapping = accrueMappingList == null ? Stream.empty() : accrueMappingList.stream();
+    if (accrueMapping == null)
+      accrueMapping = Stream.empty();
+
+    return Stream.concat(
+        accrueMapping.sorted(ScheduledChargesService::proportionalityApplicationOrder),
+        chargeMapping.sorted(ScheduledChargesService::proportionalityApplicationOrder));
+  }
+
+  static class ScheduledChargeComparator implements Comparator<ScheduledCharge>
+  {
+    @Override
+    public int compare(ScheduledCharge o1, ScheduledCharge o2) {
+      int ret = o1.getScheduledAction().when.compareTo(o2.getScheduledAction().when);
+      if (ret == 0)
+        ret = o1.getScheduledAction().action.compareTo(o2.getScheduledAction().action);
+      if (ret == 0)
+        ret = proportionalityApplicationOrder(o1.getChargeDefinition(), o2.getChargeDefinition());
+      if (ret == 0)
+        return o1.getChargeDefinition().getIdentifier().compareTo(o2.getChargeDefinition().getIdentifier());
+      else
+        return ret;
+    }
+  }
+
+  private static int proportionalityApplicationOrder(final ChargeDefinition o1, final ChargeDefinition o2) {
+    final Optional<ChargeProportionalDesignator> aProportionalToDesignator
+        = ChargeProportionalDesignator.fromString(o1.getProportionalTo());
+    final Optional<ChargeProportionalDesignator> bProportionalToDesignator
+        = ChargeProportionalDesignator.fromString(o2.getProportionalTo());
+
+    if (aProportionalToDesignator.isPresent() && bProportionalToDesignator.isPresent())
+      return Integer.compare(
+          aProportionalToDesignator.get().getOrderOfApplication(),
+          bProportionalToDesignator.get().getOrderOfApplication());
+    else if (aProportionalToDesignator.isPresent())
+      return 1;
+    else if (bProportionalToDesignator.isPresent())
+      return -1;
+    else
+      return 0;
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentEntity.java b/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentEntity.java
index b2d7e8b..a395c02 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentEntity.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentEntity.java
@@ -46,6 +46,13 @@
   public BalanceSegmentEntity() {
   }
 
+  public BalanceSegmentEntity(ProductEntity product, String segmentSetIdentifier, String segmentIdentifier, BigDecimal lowerBound) {
+    this.product = product;
+    this.segmentSetIdentifier = segmentSetIdentifier;
+    this.segmentIdentifier = segmentIdentifier;
+    this.lowerBound = lowerBound;
+  }
+
   public Long getId() {
     return id;
   }
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/ChargeRangeTest.java b/service/src/test/java/io/mifos/individuallending/internal/service/ChargeRangeTest.java
new file mode 100644
index 0000000..a581c78
--- /dev/null
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/ChargeRangeTest.java
@@ -0,0 +1,25 @@
+package io.mifos.individuallending.internal.service;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.util.Optional;
+
+public class ChargeRangeTest {
+  @Test
+  public void amountIsWithinRange() throws Exception {
+    final ChargeRange testSubject1 = new ChargeRange(BigDecimal.TEN, Optional.empty());
+    Assert.assertFalse(testSubject1.amountIsWithinRange(BigDecimal.ZERO));
+    Assert.assertFalse(testSubject1.amountIsWithinRange(BigDecimal.ONE));
+    Assert.assertTrue(testSubject1.amountIsWithinRange(BigDecimal.TEN));
+    Assert.assertTrue(testSubject1.amountIsWithinRange(BigDecimal.TEN.add(BigDecimal.ONE)));
+
+    final ChargeRange testSubject2 = new ChargeRange(BigDecimal.ZERO, Optional.of(BigDecimal.TEN));
+    Assert.assertTrue(testSubject2.amountIsWithinRange(BigDecimal.ZERO));
+    Assert.assertTrue(testSubject2.amountIsWithinRange(BigDecimal.ONE));
+    Assert.assertFalse(testSubject2.amountIsWithinRange(BigDecimal.TEN));
+    Assert.assertFalse(testSubject2.amountIsWithinRange(BigDecimal.TEN.add(BigDecimal.ONE)));
+  }
+
+}
\ No newline at end of file
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/CostComponentServiceTest.java b/service/src/test/java/io/mifos/individuallending/internal/service/CostComponentServiceTest.java
new file mode 100644
index 0000000..eab6828
--- /dev/null
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/CostComponentServiceTest.java
@@ -0,0 +1,91 @@
+package io.mifos.individuallending.internal.service;
+
+import io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import static io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator.PRINCIPAL_ADJUSTMENT_DESIGNATOR;
+import static io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator.RUNNING_BALANCE_DESIGNATOR;
+
+@RunWith(Parameterized.class)
+public class CostComponentServiceTest {
+  private static class TestCase {
+    final String description;
+    ChargeProportionalDesignator chargeProportionalDesignator = ChargeProportionalDesignator.NOT_PROPORTIONAL;
+    BigDecimal maximumBalance = BigDecimal.ZERO;
+    BigDecimal runningBalance = BigDecimal.ZERO;
+    BigDecimal loanPaymentSize = BigDecimal.ZERO;
+    BigDecimal expectedAmount = BigDecimal.ZERO;
+
+    private TestCase(String description) {
+      this.description = description;
+    }
+
+    TestCase chargeProportionalDesignator(ChargeProportionalDesignator chargeProportionalDesignator) {
+      this.chargeProportionalDesignator = chargeProportionalDesignator;
+      return this;
+    }
+
+    TestCase maximumBalance(BigDecimal maximumBalance) {
+      this.maximumBalance = maximumBalance;
+      return this;
+    }
+
+    TestCase runningBalance(BigDecimal runningBalance) {
+      this.runningBalance = runningBalance;
+      return this;
+    }
+
+    TestCase loanPaymentSize(BigDecimal loanPaymentSize) {
+      this.loanPaymentSize = loanPaymentSize;
+      return this;
+    }
+
+    TestCase expectedAmount(BigDecimal expectedAmount) {
+      this.expectedAmount = expectedAmount;
+      return this;
+    }
+  }
+
+  @Parameterized.Parameters
+  public static Collection testCases() {
+    final Collection<CostComponentServiceTest.TestCase> ret = new ArrayList<>();
+    ret.add(new TestCase("simple"));
+    ret.add(new TestCase("distribution fee")
+        .chargeProportionalDesignator(PRINCIPAL_ADJUSTMENT_DESIGNATOR)
+        .maximumBalance(BigDecimal.valueOf(2000))
+        .loanPaymentSize(BigDecimal.valueOf(-2000))
+        .expectedAmount(BigDecimal.valueOf(2000)));
+    ret.add(new TestCase("origination fee")
+        .chargeProportionalDesignator(RUNNING_BALANCE_DESIGNATOR)
+        .runningBalance(BigDecimal.valueOf(5000))
+        .expectedAmount(BigDecimal.valueOf(5000)));
+    return ret;
+  }
+
+  private final CostComponentServiceTest.TestCase testCase;
+
+  public CostComponentServiceTest(final CostComponentServiceTest.TestCase testCase) {
+    this.testCase = testCase;
+  }
+
+  @Test
+  public void getAmountProportionalTo() {
+    final BigDecimal amount = CostComponentService.getAmountProportionalTo(
+        testCase.chargeProportionalDesignator,
+        testCase.maximumBalance,
+        testCase.runningBalance,
+        testCase.loanPaymentSize,
+        Collections.emptyMap());
+
+    Assert.assertEquals(testCase.expectedAmount, amount);
+  }
+
+}
\ 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 7f73106..92002b7 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
@@ -28,6 +28,7 @@
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.Random;
 
 import static java.math.BigDecimal.ROUND_HALF_EVEN;
@@ -107,7 +108,7 @@
     chargeDefinition.setFromAccountDesignator(AccountDesignators.CUSTOMER_LOAN);
     chargeDefinition.setAccrualAccountDesignator(AccountDesignators.INTEREST_ACCRUAL);
     chargeDefinition.setToAccountDesignator(AccountDesignators.INTEREST_INCOME);
-    return new ScheduledCharge(scheduledAction, chargeDefinition);
+    return new ScheduledCharge(scheduledAction, chargeDefinition, Optional.empty());
   }
 
   static Period getPeriod(final LocalDate initialDate, final int periodBeginDelta, final int periodLength) {
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 1bb13b1..fc31ae7 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,6 +24,7 @@
 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.BalanceSegmentRepository;
 import io.mifos.portfolio.service.internal.repository.CaseEntity;
 import io.mifos.portfolio.service.internal.repository.ProductEntity;
 import io.mifos.portfolio.service.internal.service.ChargeDefinitionService;
@@ -31,6 +32,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.mockito.Matchers;
 import org.mockito.Mockito;
 
 import java.math.BigDecimal;
@@ -47,6 +49,7 @@
  */
 @RunWith(Parameterized.class)
 public class IndividualLoanServiceTest {
+
   private static class ActionDatePair {
     final Action action;
     final LocalDate localDate;
@@ -171,6 +174,7 @@
 
   private final TestCase testCase;
   private final IndividualLoanService testSubject;
+  private final ScheduledChargesService scheduledChargesService;
   private final Map<String, List<ChargeDefinition>> chargeDefinitionsByChargeAction;
   private final Map<String, List<ChargeDefinition>> chargeDefinitionsByAccrueAction;
 
@@ -282,7 +286,12 @@
     Mockito.doReturn(chargeDefinitionsByChargeAction).when(chargeDefinitionServiceMock).getChargeDefinitionsMappedByChargeAction(testCase.productIdentifier);
     Mockito.doReturn(chargeDefinitionsByAccrueAction).when(chargeDefinitionServiceMock).getChargeDefinitionsMappedByAccrueAction(testCase.productIdentifier);
 
-    testSubject = new IndividualLoanService(chargeDefinitionServiceMock);
+    final BalanceSegmentRepository balanceSegmentRepositoryMock = Mockito.mock(BalanceSegmentRepository.class);
+    Mockito.doReturn(Stream.empty()).when(balanceSegmentRepositoryMock).findByProductIdentifierAndSegmentSetIdentifier(Matchers.anyString(), Matchers.anyString());
+
+    scheduledChargesService = new ScheduledChargesService(chargeDefinitionServiceMock, balanceSegmentRepositoryMock);
+
+    testSubject = new IndividualLoanService(scheduledChargesService);
   }
 
   @Test
@@ -376,7 +385,7 @@
   @Test
   public void getScheduledCharges() {
     final List<ScheduledAction> scheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(testCase.initialDisbursementDate, testCase.caseParameters);
-    final List<ScheduledCharge> scheduledCharges = testSubject.getScheduledCharges(testCase.productIdentifier,
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(testCase.productIdentifier,
         scheduledActions);
 
     final List<LocalDate> interestCalculationDates = scheduledCharges.stream()
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/ScheduledChargesServiceTest.java b/service/src/test/java/io/mifos/individuallending/internal/service/ScheduledChargesServiceTest.java
new file mode 100644
index 0000000..032e223
--- /dev/null
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/ScheduledChargesServiceTest.java
@@ -0,0 +1,111 @@
+package io.mifos.individuallending.internal.service;
+
+import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentEntity;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentRepository;
+import io.mifos.portfolio.service.internal.service.ChargeDefinitionService;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mockito;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+@RunWith(Parameterized.class)
+public class ScheduledChargesServiceTest {
+
+  private static final String PRODUCT_IDENTIFIER = "a";
+  private static final String SEGMENT_SET_IDENTIFIER = "b";
+
+  static class TestCase {
+    final String description;
+    List<BalanceSegmentEntity> balanceSegmentEntities = Collections.emptyList();
+    String fromSegment = null;
+    String toSegment = null;
+    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+    Optional<ChargeRange> expectedResult = Optional.empty();
+
+    private TestCase(String description) {
+      this.description = description;
+    }
+
+    TestCase balanceSegmentEntities(List<BalanceSegmentEntity> balanceSegmentEntities) {
+      this.balanceSegmentEntities = balanceSegmentEntities;
+      return this;
+    }
+
+    TestCase fromSegment(String fromSegment) {
+      this.fromSegment = fromSegment;
+      return this;
+    }
+
+    TestCase toSegment(String toSegment) {
+      this.toSegment = toSegment;
+      return this;
+    }
+
+    TestCase expectedResult(@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional<ChargeRange> expectedResult) {
+      this.expectedResult = expectedResult;
+      return this;
+    }
+  }
+
+  @Parameterized.Parameters
+  public static Collection testCases() {
+    final Collection<ScheduledChargesServiceTest.TestCase> ret = new ArrayList<>();
+    ret.add(new ScheduledChargesServiceTest.TestCase("simple"));
+    ret.add(new TestCase("two segments, one referenced")
+        .balanceSegmentEntities(Arrays.asList(
+            new BalanceSegmentEntity(null, SEGMENT_SET_IDENTIFIER, "first", BigDecimal.ZERO),
+            new BalanceSegmentEntity(null, SEGMENT_SET_IDENTIFIER, "second", BigDecimal.TEN)))
+        .fromSegment("first")
+        .toSegment("first")
+        .expectedResult(Optional.of(new ChargeRange(BigDecimal.ZERO, Optional.of(BigDecimal.TEN))))
+    );
+    ret.add(new TestCase("two segments, both referenced")
+        .balanceSegmentEntities(Arrays.asList(
+            new BalanceSegmentEntity(null, SEGMENT_SET_IDENTIFIER, "lower", BigDecimal.ZERO),
+            new BalanceSegmentEntity(null, SEGMENT_SET_IDENTIFIER, "higher", BigDecimal.TEN)))
+        .fromSegment("lower")
+        .toSegment("higher")
+        .expectedResult(Optional.of(new ChargeRange(BigDecimal.ZERO, Optional.empty())))
+    );
+    ret.add(new TestCase("two segments, one mis-referenced")
+        .balanceSegmentEntities(Arrays.asList(
+            new BalanceSegmentEntity(null, SEGMENT_SET_IDENTIFIER, "first", BigDecimal.ZERO),
+            new BalanceSegmentEntity(null, SEGMENT_SET_IDENTIFIER, "second", BigDecimal.TEN)))
+        .fromSegment("first")
+        .toSegment("second2")
+        .expectedResult(Optional.empty())
+    );
+    return ret;
+  }
+
+  private final ScheduledChargesServiceTest.TestCase testCase;
+
+  public ScheduledChargesServiceTest(final ScheduledChargesServiceTest.TestCase testCase) {
+    this.testCase = testCase;
+  }
+
+  @Test
+  public void findChargeRange() throws Exception {
+
+    final ChargeDefinitionService chargeDefinitionServiceMock = Mockito.mock(ChargeDefinitionService.class);
+    final BalanceSegmentRepository balanceSegmentRepositoryMock = Mockito.mock(BalanceSegmentRepository.class);
+
+    Mockito.doReturn(testCase.balanceSegmentEntities.stream())
+        .when(balanceSegmentRepositoryMock)
+        .findByProductIdentifierAndSegmentSetIdentifier(PRODUCT_IDENTIFIER, SEGMENT_SET_IDENTIFIER);
+
+    final ScheduledChargesService testSubject = new ScheduledChargesService(chargeDefinitionServiceMock, balanceSegmentRepositoryMock);
+    final ChargeDefinition chargeDefinition = new ChargeDefinition();
+
+    chargeDefinition.setForSegmentSet(SEGMENT_SET_IDENTIFIER);
+    chargeDefinition.setFromSegment(testCase.fromSegment);
+    chargeDefinition.setToSegment(testCase.toSegment);
+    final Optional<ChargeRange> result = testSubject.findChargeRange(PRODUCT_IDENTIFIER, chargeDefinition);
+    Assert.assertEquals(testCase.expectedResult, result);
+  }
+}
\ No newline at end of file