Repayment of full amount now works.
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/product/ChargeIdentifiers.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/product/ChargeIdentifiers.java
index c0e0c56..9270c92 100644
--- a/api/src/main/java/io/mifos/individuallending/api/v1/domain/product/ChargeIdentifiers.java
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/domain/product/ChargeIdentifiers.java
@@ -48,6 +48,7 @@
   String TRACK_RETURN_PRINCIPAL_ID = "track-return-principal";
   String MAXIMUM_BALANCE_DESIGNATOR = "{maximumbalance}";
   String RUNNING_BALANCE_DESIGNATOR = "{runningbalance}";
+  String REPAYMENT_DESIGNATOR = "{repayment}";
   String PRINCIPAL_ADJUSTMENT_DESIGNATOR = "{principaladjustment}";
 
   static String nameToIdentifier(String name) {
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 472fc5d..8eab0e9 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
@@ -246,12 +246,12 @@
 
     final Set<Debtor> debtors = new HashSet<>();
     debtors.add(new Debtor(
-        AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER,
+        customerLoanAccountIdentifier,
         calculatedInterest.toPlainString()));
 
     final Set<Creditor> creditors = new HashSet<>();
     creditors.add(new Creditor(
-        customerLoanAccountIdentifier,
+        AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER,
         calculatedInterest.toPlainString()));
     AccountingFixture.verifyTransfer(ledgerManager, debtors, creditors);
 
@@ -274,15 +274,17 @@
     checkNextActionsCorrect(product.getIdentifier(), customerCase.getIdentifier(), Action.APPLY_INTEREST,
         Action.APPLY_INTEREST, Action.MARK_LATE, Action.ACCEPT_PAYMENT, Action.DISBURSE, Action.WRITE_OFF, Action.CLOSE);
 
+    final BigDecimal principal = expectedCurrentBalance.subtract(interestAccrued);
+
     final Set<Debtor> debtors = new HashSet<>();
-    debtors.add(new Debtor(AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER, interestAccrued.toPlainString()));
-    debtors.add(new Debtor(AccountingFixture.LOANS_PAYABLE_ACCOUNT_IDENTIFIER, expectedCurrentBalance.subtract(interestAccrued).toPlainString()));
     debtors.add(new Debtor(customerLoanAccountIdentifier, expectedCurrentBalance.toPlainString()));
+    debtors.add(new Debtor(AccountingFixture.LOAN_FUNDS_SOURCE_ACCOUNT_IDENTIFIER, principal.toPlainString()));
+    debtors.add(new Debtor(AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER, interestAccrued.toPlainString()));
 
     final Set<Creditor> creditors = new HashSet<>();
-    creditors.add(new Creditor(AccountingFixture.CONSUMER_LOAN_INTEREST_ACCOUNT_IDENTIFIER, interestAccrued.toPlainString()));
-    creditors.add(new Creditor(AccountingFixture.LOAN_FUNDS_SOURCE_ACCOUNT_IDENTIFIER, expectedCurrentBalance.subtract(interestAccrued).toPlainString()));
     creditors.add(new Creditor(AccountingFixture.TELLER_ONE_ACCOUNT_IDENTIFIER, expectedCurrentBalance.toPlainString()));
+    creditors.add(new Creditor(AccountingFixture.LOANS_PAYABLE_ACCOUNT_IDENTIFIER, principal.toPlainString()));
+    creditors.add(new Creditor(AccountingFixture.CONSUMER_LOAN_INTEREST_ACCOUNT_IDENTIFIER, interestAccrued.toPlainString()));
 
     AccountingFixture.verifyTransfer(ledgerManager, debtors, creditors);
 
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 6a30102..2bbea7c 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -173,11 +173,11 @@
         INTEREST_NAME,
         Action.ACCEPT_PAYMENT,
         BigDecimal.valueOf(0.05),
-        INTEREST_ACCRUAL,
+        CUSTOMER_LOAN,
         INTEREST_INCOME);
     interestCharge.setForCycleSizeUnit(ChronoUnit.YEARS);
     interestCharge.setAccrueAction(Action.APPLY_INTEREST.name());
-    interestCharge.setAccrualAccountDesignator(CUSTOMER_LOAN);
+    interestCharge.setAccrualAccountDesignator(INTEREST_ACCRUAL);
     interestCharge.setProportionalTo(ChargeIdentifiers.RUNNING_BALANCE_DESIGNATOR);
 
     final ChargeDefinition customerRepaymentCharge = new ChargeDefinition();
@@ -187,20 +187,20 @@
     customerRepaymentCharge.setDescription(REPAYMENT_NAME);
     customerRepaymentCharge.setFromAccountDesignator(CUSTOMER_LOAN);
     customerRepaymentCharge.setToAccountDesignator(ENTRY);
-    customerRepaymentCharge.setProportionalTo(ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
+    customerRepaymentCharge.setProportionalTo(ChargeIdentifiers.REPAYMENT_DESIGNATOR);
     customerRepaymentCharge.setChargeMethod(ChargeDefinition.ChargeMethod.PROPORTIONAL);
     customerRepaymentCharge.setAmount(BigDecimal.ONE);
 
-    final ChargeDefinition trackPrincipalRepaymentCharge = new ChargeDefinition();
-    trackPrincipalRepaymentCharge.setChargeAction(Action.ACCEPT_PAYMENT.name());
-    trackPrincipalRepaymentCharge.setIdentifier(TRACK_RETURN_PRINCIPAL_ID);
-    trackPrincipalRepaymentCharge.setName(TRACK_RETURN_PRINCIPAL_NAME);
-    trackPrincipalRepaymentCharge.setDescription(TRACK_RETURN_PRINCIPAL_NAME);
-    trackPrincipalRepaymentCharge.setFromAccountDesignator(LOANS_PAYABLE);
-    trackPrincipalRepaymentCharge.setToAccountDesignator(LOAN_FUNDS_SOURCE);
-    trackPrincipalRepaymentCharge.setProportionalTo(ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
-    trackPrincipalRepaymentCharge.setChargeMethod(ChargeDefinition.ChargeMethod.PROPORTIONAL);
-    trackPrincipalRepaymentCharge.setAmount(BigDecimal.ONE);
+    final ChargeDefinition trackReturnPrincipalCharge = new ChargeDefinition();
+    trackReturnPrincipalCharge.setChargeAction(Action.ACCEPT_PAYMENT.name());
+    trackReturnPrincipalCharge.setIdentifier(TRACK_RETURN_PRINCIPAL_ID);
+    trackReturnPrincipalCharge.setName(TRACK_RETURN_PRINCIPAL_NAME);
+    trackReturnPrincipalCharge.setDescription(TRACK_RETURN_PRINCIPAL_NAME);
+    trackReturnPrincipalCharge.setFromAccountDesignator(LOAN_FUNDS_SOURCE);
+    trackReturnPrincipalCharge.setToAccountDesignator(LOANS_PAYABLE);
+    trackReturnPrincipalCharge.setProportionalTo(ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
+    trackReturnPrincipalCharge.setChargeMethod(ChargeDefinition.ChargeMethod.PROPORTIONAL);
+    trackReturnPrincipalCharge.setAmount(BigDecimal.ONE);
 
     final ChargeDefinition disbursementReturnCharge = charge(
             RETURN_DISBURSEMENT_NAME,
@@ -220,7 +220,7 @@
     ret.add(writeOffAllowanceCharge);
     ret.add(interestCharge);
     ret.add(customerRepaymentCharge);
-    ret.add(trackPrincipalRepaymentCharge);
+    ret.add(trackReturnPrincipalCharge);
     ret.add(disbursementReturnCharge);
 
     return ret;
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 14ef0f9..e62327e 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
@@ -52,6 +52,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -103,6 +104,8 @@
             Action.OPEN,
             entry,
             designatorToAccountIdentifierMapper))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
         .collect(Collectors.toList());
 
     accountingAdapter.bookCharges(charges,
@@ -142,6 +145,8 @@
             Action.DENY,
             entry,
             designatorToAccountIdentifierMapper))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
         .collect(Collectors.toList());
 
     final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
@@ -187,6 +192,8 @@
             Action.APPROVE,
             entry,
             designatorToAccountIdentifierMapper))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
         .collect(Collectors.toList());
 
     accountingAdapter.bookCharges(charges,
@@ -226,7 +233,9 @@
               .map(entry -> mapCostComponentEntryToChargeInstance(
                   Action.DISBURSE,
                   entry,
-                  designatorToAccountIdentifierMapper)),
+                  designatorToAccountIdentifierMapper))
+              .filter(Optional::isPresent)
+              .map(Optional::get),
           Stream.of(getDisbursalChargeInstance(disbursalAmount, designatorToAccountIdentifierMapper)))
         .collect(Collectors.toList());
 
@@ -275,6 +284,8 @@
             Action.APPLY_INTEREST,
             entry,
             designatorToAccountIdentifierMapper))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
         .collect(Collectors.toList());
 
     accountingAdapter.bookCharges(charges,
@@ -319,6 +330,8 @@
             Action.ACCEPT_PAYMENT,
             entry,
             designatorToAccountIdentifierMapper))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
         .collect(Collectors.toList());
 
 
@@ -384,30 +397,38 @@
     return new IndividualLoanCommandEvent(command.getProductIdentifier(), command.getCaseIdentifier());
   }
 
-  private static ChargeInstance mapCostComponentEntryToChargeInstance(
+  private static Optional<ChargeInstance> mapCostComponentEntryToChargeInstance(
       final Action action,
       final Map.Entry<ChargeDefinition, CostComponent> costComponentEntry,
       final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper) {
     final ChargeDefinition chargeDefinition = costComponentEntry.getKey();
     final BigDecimal chargeAmount = costComponentEntry.getValue().getAmount();
 
-    if (chargeDefinition.getAccrualAccountDesignator() != null) {
+    if (chargeIsAccrued(chargeDefinition)) {
       if (Action.valueOf(chargeDefinition.getAccrueAction()) == action)
-        return new ChargeInstance(
+        return Optional.of(new ChargeInstance(
             designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getFromAccountDesignator()),
             designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getAccrualAccountDesignator()),
-            chargeAmount);
-      else
-        return new ChargeInstance(
-            designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getToAccountDesignator()),
+            chargeAmount));
+      else if (Action.valueOf(chargeDefinition.getChargeAction()) == action)
+        return Optional.of(new ChargeInstance(
             designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getAccrualAccountDesignator()),
-            chargeAmount);
+            designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getToAccountDesignator()),
+            chargeAmount));
+      else
+        return Optional.empty();
     }
-    else
-      return new ChargeInstance(
+    else if (Action.valueOf(chargeDefinition.getChargeAction()) == action)
+      return Optional.of(new ChargeInstance(
           designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getFromAccountDesignator()),
           designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getToAccountDesignator()),
-          chargeAmount);
+          chargeAmount));
+    else
+      return Optional.empty();
+  }
+
+  private static boolean chargeIsAccrued(final ChargeDefinition chargeDefinition) {
+    return chargeDefinition.getAccrualAccountDesignator() != null;
   }
 
   private static ChargeInstance getDisbursalChargeInstance(
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 5c871ba..7a1a6e5 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
@@ -42,7 +42,7 @@
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.*;
-import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -52,6 +52,7 @@
 public class CostComponentService {
   private static final int EXTRA_PRECISION = 4;
   private static final int RUNNING_CALCULATION_PRECISION = 8;
+  private static final String NOT_PROPORTIONAL = "null";
 
   private final ProductRepository productRepository;
   private final CaseRepository caseRepository;
@@ -195,7 +196,7 @@
 
 
     final Map<Boolean, List<ScheduledCharge>> chargesSplitIntoScheduledAndAccrued = scheduledCharges.stream()
-        .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x, Action.DISBURSE)));
+        .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x.getChargeDefinition(), Action.DISBURSE)));
 
     final Map<ChargeDefinition, CostComponent> accruedCostComponents =
         optionalStartOfTerm.map(startOfTerm ->
@@ -239,7 +240,7 @@
         Collections.singletonList(interestAction));
 
     final Map<Boolean, List<ScheduledCharge>> chargesSplitIntoScheduledAndAccrued = scheduledCharges.stream()
-        .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x, Action.APPLY_INTEREST)));
+        .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x.getChargeDefinition(), Action.APPLY_INTEREST)));
 
     final Map<ChargeDefinition, CostComponent> accruedCostComponents = chargesSplitIntoScheduledAndAccrued.get(true)
         .stream()
@@ -295,7 +296,7 @@
         Collections.singletonList(scheduledAction));
 
     final Map<Boolean, List<ScheduledCharge>> chargesSplitIntoScheduledAndAccrued = scheduledChargesForThisAction.stream()
-        .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x, Action.ACCEPT_PAYMENT)));
+        .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x.getChargeDefinition(), Action.ACCEPT_PAYMENT)));
 
     final Map<ChargeDefinition, CostComponent> accruedCostComponents = chargesSplitIntoScheduledAndAccrued.get(true)
         .stream()
@@ -314,9 +315,19 @@
         minorCurrencyUnitDigits);
   }
 
-  private static boolean isAccruedChargeForAction(final ScheduledCharge scheduledCharge, final Action action) {
-    return scheduledCharge.getChargeDefinition().getAccrueAction() != null &&
-        scheduledCharge.getChargeDefinition().getChargeAction().equals(action.name());
+  private static boolean isAccruedChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
+    return chargeDefinition.getAccrueAction() != null &&
+        chargeDefinition.getChargeAction().equals(action.name());
+  }
+
+  private static boolean isAccrualChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
+    return chargeDefinition.getAccrueAction() != null &&
+        chargeDefinition.getAccrueAction().equals(action.name());
+  }
+
+  private static boolean isOneOffChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
+    return chargeDefinition.getChargeAction() != null &&
+        chargeDefinition.getChargeAction().equals(action.name());
   }
 
   private CostComponent getAccruedCostComponentToApply(final DataContextOfAction dataContextOfAction,
@@ -385,35 +396,86 @@
         balanceAdjustment = balanceAdjustment.add(entry.getValue().getAmount());
     }
 
-    final Map<Boolean, List<ScheduledCharge>> partitionedCharges = scheduledCharges.stream()
-        .collect(Collectors.partitioningBy(CostComponentService::proportionalToPrincipalAdjustment));
+    final Map<String, List<ScheduledCharge>> partitionedCharges = scheduledCharges.stream()
+        .collect(Collectors.groupingBy(CostComponentService::proportionalToDesignator));
 
-    for (final ScheduledCharge scheduledCharge : partitionedCharges.get(false))
+    final List<String> orderOfChargesByDesignatorFirstSet = Arrays.asList(
+        NOT_PROPORTIONAL,
+        ChargeIdentifiers.MAXIMUM_BALANCE_DESIGNATOR,
+        ChargeIdentifiers.RUNNING_BALANCE_DESIGNATOR);
+
+    for (final String chargeProportionalTo : orderOfChargesByDesignatorFirstSet)
     {
-      final CostComponent costComponent = costComponentMap
-          .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
+      final BigDecimal amountProportionalTo;
+      switch (chargeProportionalTo) {
+        case NOT_PROPORTIONAL:
+          amountProportionalTo = BigDecimal.ZERO;
+          break;
+        case ChargeIdentifiers.MAXIMUM_BALANCE_DESIGNATOR:
+          amountProportionalTo = maximumBalance;
+          break;
+        case ChargeIdentifiers.RUNNING_BALANCE_DESIGNATOR:
+          amountProportionalTo = runningBalance;
+          break;
+        default:
+          amountProportionalTo = BigDecimal.ZERO;
+          break;
+      }
+//TODO: correctly implement charges which are proportionate to other charges.
 
-      final BigDecimal chargeAmount = howToApplyScheduledChargeToBalance(scheduledCharge)
-          .apply(maximumBalance, currentRunningBalance)
-          .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
-      if (chargeDefinitionTouchesAccount(scheduledCharge.getChargeDefinition(), AccountDesignators.CUSTOMER_LOAN))
-        balanceAdjustment = balanceAdjustment.add(chargeAmount);
-      costComponent.setAmount(costComponent.getAmount().add(chargeAmount));
-      currentRunningBalance = currentRunningBalance.add(chargeAmount);
+      final List<ScheduledCharge> partition = partitionedCharges.get(chargeProportionalTo);
+      if (partition != null) {
+        for (final ScheduledCharge scheduledCharge : partition) {
+          final CostComponent costComponent = costComponentMap
+              .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
+
+          final BigDecimal chargeAmount = howToApplyScheduledChargeToAmount(scheduledCharge)
+              .apply(amountProportionalTo)
+              .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
+          if (chargeDefinitionTouchesAccount(scheduledCharge.getChargeDefinition(), AccountDesignators.CUSTOMER_LOAN))
+            balanceAdjustment = balanceAdjustment.add(chargeAmount);
+          costComponent.setAmount(costComponent.getAmount().add(chargeAmount));
+          currentRunningBalance = currentRunningBalance.add(chargeAmount);
+        }
+      }
     }
 
-    final BigDecimal principalAdjustment = loanPaymentSize.subtract(balanceAdjustment);
-    for (final ScheduledCharge scheduledCharge : partitionedCharges.get(true))
-    {
-      final CostComponent costComponent = costComponentMap
-          .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
+    final List<String> orderOfChargesByDesignatorSecondSet = Arrays.asList(
+        ChargeIdentifiers.REPAYMENT_DESIGNATOR,
+        ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
 
-      final BigDecimal chargeAmount = applyPrincipalAdjustmentCharge(scheduledCharge, principalAdjustment)
-          .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
-      if (chargeDefinitionTouchesAccount(scheduledCharge.getChargeDefinition(), AccountDesignators.CUSTOMER_LOAN))
-        balanceAdjustment = balanceAdjustment.add(chargeAmount);
-      costComponent.setAmount(costComponent.getAmount().add(chargeAmount));
-      currentRunningBalance = currentRunningBalance.add(chargeAmount);
+
+    final BigDecimal principalAdjustment = loanPaymentSize.subtract(balanceAdjustment);
+    for (final String chargeProportionalTo : orderOfChargesByDesignatorSecondSet)
+    {
+      final BigDecimal amountProportionalTo;
+      switch (chargeProportionalTo) {
+        case ChargeIdentifiers.REPAYMENT_DESIGNATOR:
+          amountProportionalTo = loanPaymentSize;
+          break;
+        case ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR:
+          amountProportionalTo = principalAdjustment;
+          break;
+        default:
+          amountProportionalTo = BigDecimal.ZERO;
+          break;
+      }
+
+      final List<ScheduledCharge> partition = partitionedCharges.get(chargeProportionalTo);
+      if (partition != null) {
+        for (final ScheduledCharge scheduledCharge : partition) {
+          final CostComponent costComponent = costComponentMap
+              .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
+
+          final BigDecimal chargeAmount = howToApplyScheduledChargeToAmount(scheduledCharge)
+              .apply(amountProportionalTo)
+              .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
+          if (chargeDefinitionTouchesAccount(scheduledCharge.getChargeDefinition(), AccountDesignators.CUSTOMER_LOAN))
+            balanceAdjustment = balanceAdjustment.add(chargeAmount);
+          costComponent.setAmount(costComponent.getAmount().add(chargeAmount));
+          currentRunningBalance = currentRunningBalance.add(chargeAmount);
+        }
+      }
     }
 
     return new CostComponentsForRepaymentPeriod(
@@ -422,12 +484,6 @@
         balanceAdjustment.negate());
   }
 
-  private static BigDecimal applyPrincipalAdjustmentCharge(
-      final ScheduledCharge scheduledCharge,
-      final BigDecimal principalAdjustment) {
-    return scheduledCharge.getChargeDefinition().getAmount().multiply(principalAdjustment);
-  }
-
   private static CostComponent constructEmptyCostComponent(final ChargeDefinition chargeDefinition) {
     final CostComponent ret = new CostComponent();
     ret.setChargeIdentifier(chargeDefinition.getIdentifier());
@@ -435,42 +491,33 @@
     return ret;
   }
 
-  private static boolean proportionalToPrincipalAdjustment(final ScheduledCharge scheduledCharge) {
+  private static String proportionalToDesignator(final ScheduledCharge scheduledCharge) {
     if (!scheduledCharge.getChargeDefinition().getChargeMethod().equals(ChargeDefinition.ChargeMethod.PROPORTIONAL))
-      return false;
-    final String proportionalTo = scheduledCharge.getChargeDefinition().getProportionalTo();
-    return proportionalTo != null && proportionalTo.equals(ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
+      return NOT_PROPORTIONAL;
+
+    return scheduledCharge.getChargeDefinition().getProportionalTo();
   }
 
-  private static BiFunction<BigDecimal, BigDecimal, BigDecimal> howToApplyScheduledChargeToBalance(
+  private static Function<BigDecimal, BigDecimal> howToApplyScheduledChargeToAmount(
       final ScheduledCharge scheduledCharge)
   {
-
+    final ChargeDefinition chargeDefinition = scheduledCharge.getChargeDefinition();
+    final Action action = scheduledCharge.getScheduledAction().action;
     switch (scheduledCharge.getChargeDefinition().getChargeMethod())
     {
       case FIXED:
-        return (maximumBalance, runningBalance) -> scheduledCharge.getChargeDefinition().getAmount();
-      case PROPORTIONAL: {
-        switch (scheduledCharge.getChargeDefinition().getProportionalTo()) {
-          case ChargeIdentifiers.RUNNING_BALANCE_DESIGNATOR:
-            return (maximumBalance, runningBalance) ->
-                PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
-                    .multiply(runningBalance);
-          case ChargeIdentifiers.MAXIMUM_BALANCE_DESIGNATOR:
-            return (maximumBalance, runningBalance) ->
-                PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
-                    .multiply(maximumBalance);
-          case ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR: //This is handled elsewhere.
-            throw new IllegalStateException("A principal adjustment charge should not be passed to the same application function as the other charges.");
-          default:
-//TODO: correctly implement charges which are proportionate to other charges.
-            return (maximumBalance, runningBalance) ->
-                PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
-                    .multiply(maximumBalance);
-        }
-      }
+        return (amountProportionalTo) -> scheduledCharge.getChargeDefinition().getAmount();
+      case PROPORTIONAL:
+        if (isAccrualChargeForAction(chargeDefinition, action))
+          return (amountProportionalTo) ->
+              PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
+                  .multiply(amountProportionalTo);
+        else if (isOneOffChargeForAction(chargeDefinition, action))
+            return (amountProportionalTo) ->
+                scheduledCharge.getChargeDefinition().getAmount()
+                    .multiply(amountProportionalTo);
       default:
-        return (maximumBalance, runningBalance) -> BigDecimal.ZERO;
+        return (amountProportionalTo) -> BigDecimal.ZERO;
     }
   }