Routing the size of the payment through with the command so that the
user can repay more or less than the pre-calculated amount.
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/domain/Command.java b/api/src/main/java/io/mifos/portfolio/api/v1/domain/Command.java
index 76eb969..cc28b11 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/domain/Command.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/domain/Command.java
@@ -17,6 +17,7 @@
 
 import javax.annotation.Nullable;
 import javax.validation.Valid;
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Objects;
 
@@ -30,7 +31,7 @@
 
   @Valid
   @Nullable
-  private List<CostComponent> costComponents;
+  private BigDecimal paymentSize;
 
   private String note;
   private String createdOn;
@@ -47,12 +48,13 @@
     this.oneTimeAccountAssignments = oneTimeAccountAssignments;
   }
 
-  public List<CostComponent> getCostComponents() {
-    return costComponents;
+  @Nullable
+  public BigDecimal getPaymentSize() {
+    return paymentSize;
   }
 
-  public void setCostComponents(List<CostComponent> costComponents) {
-    this.costComponents = costComponents;
+  public void setPaymentSize(@Nullable BigDecimal paymentSize) {
+    this.paymentSize = paymentSize;
   }
 
   public String getNote() {
@@ -85,20 +87,20 @@
     if (o == null || getClass() != o.getClass()) return false;
     Command command = (Command) o;
     return Objects.equals(oneTimeAccountAssignments, command.oneTimeAccountAssignments) &&
-        Objects.equals(costComponents, command.costComponents) &&
+        Objects.equals(paymentSize, command.paymentSize) &&
         Objects.equals(note, command.note);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(oneTimeAccountAssignments, costComponents, note);
+    return Objects.hash(oneTimeAccountAssignments, paymentSize, note);
   }
 
   @Override
   public String toString() {
     return "Command{" +
         "oneTimeAccountAssignments=" + oneTimeAccountAssignments +
-        ", costComponents=" + costComponents +
+        ", paymentSize=" + paymentSize +
         ", note='" + note + '\'' +
         ", createdOn='" + createdOn + '\'' +
         ", createdBy='" + createdBy + '\'' +
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 437276d..2fd1429 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
@@ -195,8 +195,19 @@
                           final List<AccountAssignment> oneTimeAccountAssignments,
                           final String event,
                           final Case.State nextState) throws InterruptedException {
+    checkStateTransfer(productIdentifier, caseIdentifier, action, oneTimeAccountAssignments, BigDecimal.ZERO, event, nextState);
+  }
+
+  void checkStateTransfer(final String productIdentifier,
+                          final String caseIdentifier,
+                          final Action action,
+                          final List<AccountAssignment> oneTimeAccountAssignments,
+                          final BigDecimal paymentSize,
+                          final String event,
+                          final Case.State nextState) throws InterruptedException {
     final Command command = new Command();
     command.setOneTimeAccountAssignments(oneTimeAccountAssignments);
+    command.setPaymentSize(paymentSize);
     portfolioManager.executeCaseCommand(productIdentifier, caseIdentifier, action.name(), command);
 
     Assert.assertTrue(eventRecorder.wait(event, new IndividualLoanCommandEvent(productIdentifier, caseIdentifier)));
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 2ad7ab3..472fc5d 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
@@ -268,6 +268,7 @@
         customerCase.getIdentifier(),
         Action.ACCEPT_PAYMENT,
         Collections.singletonList(assignEntryToTeller()),
+        expectedCurrentBalance,
         IndividualLoanEventConstants.ACCEPT_PAYMENT_INDIVIDUALLOAN_CASE,
         Case.State.ACTIVE); //Close has to be done explicitly.
     checkNextActionsCorrect(product.getIdentifier(), customerCase.getIdentifier(), Action.APPLY_INTEREST,
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 41c3227..14ef0f9 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
@@ -102,7 +102,6 @@
         .map(entry -> mapCostComponentEntryToChargeInstance(
             Action.OPEN,
             entry,
-            getRequestedChargeAmounts(command.getCommand().getCostComponents()),
             designatorToAccountIdentifierMapper))
         .collect(Collectors.toList());
 
@@ -142,7 +141,6 @@
         .map(entry -> mapCostComponentEntryToChargeInstance(
             Action.DENY,
             entry,
-            getRequestedChargeAmounts(command.getCommand().getCostComponents()),
             designatorToAccountIdentifierMapper))
         .collect(Collectors.toList());
 
@@ -188,7 +186,6 @@
         .map(entry -> mapCostComponentEntryToChargeInstance(
             Action.APPROVE,
             entry,
-            getRequestedChargeAmounts(command.getCommand().getCostComponents()),
             designatorToAccountIdentifierMapper))
         .collect(Collectors.toList());
 
@@ -229,7 +226,6 @@
               .map(entry -> mapCostComponentEntryToChargeInstance(
                   Action.DISBURSE,
                   entry,
-                  getRequestedChargeAmounts(command.getCommand().getCostComponents()),
                   designatorToAccountIdentifierMapper)),
           Stream.of(getDisbursalChargeInstance(disbursalAmount, designatorToAccountIdentifierMapper)))
         .collect(Collectors.toList());
@@ -278,7 +274,6 @@
         .map(entry -> mapCostComponentEntryToChargeInstance(
             Action.APPLY_INTEREST,
             entry,
-            Collections.emptyMap(),
             designatorToAccountIdentifierMapper))
         .collect(Collectors.toList());
 
@@ -309,16 +304,11 @@
           "End of term not set for active case ''{0}.{1}.''", productIdentifier, caseIdentifier);
 
     final CostComponentsForRepaymentPeriod costComponentsForRepaymentPeriod =
-        costComponentService.getCostComponentsForAcceptPayment(dataContextOfAction);
-
-    final Map<String, BigDecimal> requestedChargeAmounts
-        = getRequestedChargeAmounts(command.getCommand().getCostComponents());
+        costComponentService.getCostComponentsForAcceptPayment(dataContextOfAction, command.getCommand().getPaymentSize());
 
     final BigDecimal sumOfAdjustments = costComponentsForRepaymentPeriod.stream()
         .filter(entry -> entry.getKey().getIdentifier().equals(ChargeIdentifiers.REPAYMENT_ID))
-        .map(entry -> getChargeAmount(
-            requestedChargeAmounts.get(entry.getKey().getIdentifier()),
-            entry.getValue().getAmount()))
+        .map(entry -> entry.getValue().getAmount())
         .reduce(BigDecimal.ZERO, BigDecimal::add);
 
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
@@ -328,7 +318,6 @@
         .map(entry -> mapCostComponentEntryToChargeInstance(
             Action.ACCEPT_PAYMENT,
             entry,
-            requestedChargeAmounts,
             designatorToAccountIdentifierMapper))
         .collect(Collectors.toList());
 
@@ -398,38 +387,27 @@
   private static ChargeInstance mapCostComponentEntryToChargeInstance(
       final Action action,
       final Map.Entry<ChargeDefinition, CostComponent> costComponentEntry,
-      final Map<String, BigDecimal> requestedChargeAmounts,
       final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper) {
     final ChargeDefinition chargeDefinition = costComponentEntry.getKey();
-
-    final BigDecimal requestedChargeAmount = requestedChargeAmounts.get(chargeDefinition.getIdentifier());
-    final BigDecimal configuredChargeAmount = costComponentEntry.getValue().getAmount();
-
-    final BigDecimal finalChargeAmount = getChargeAmount(requestedChargeAmount, configuredChargeAmount);
+    final BigDecimal chargeAmount = costComponentEntry.getValue().getAmount();
 
     if (chargeDefinition.getAccrualAccountDesignator() != null) {
       if (Action.valueOf(chargeDefinition.getAccrueAction()) == action)
         return new ChargeInstance(
             designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getFromAccountDesignator()),
             designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getAccrualAccountDesignator()),
-            finalChargeAmount);
+            chargeAmount);
       else
         return new ChargeInstance(
             designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getToAccountDesignator()),
             designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getAccrualAccountDesignator()),
-            finalChargeAmount);
+            chargeAmount);
     }
     else
       return new ChargeInstance(
           designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getFromAccountDesignator()),
           designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getToAccountDesignator()),
-          finalChargeAmount);
-  }
-
-  private static BigDecimal getChargeAmount(
-      final BigDecimal requestedChargeAmount,
-      final BigDecimal configuredChargeAmount) {
-    return requestedChargeAmount != null ? requestedChargeAmount : configuredChargeAmount;
+          chargeAmount);
   }
 
   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 48c4a82..5c871ba 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
@@ -30,10 +30,13 @@
 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;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Nullable;
+import javax.money.MonetaryAmount;
 import java.math.BigDecimal;
 import java.time.Clock;
 import java.time.LocalDate;
@@ -47,6 +50,7 @@
  */
 @Service
 public class CostComponentService {
+  private static final int EXTRA_PRECISION = 4;
   private static final int RUNNING_CALCULATION_PRECISION = 8;
 
   private final ProductRepository productRepository;
@@ -106,7 +110,7 @@
       case APPLY_INTEREST:
         return getCostComponentsForApplyInterest(dataContextOfAction);
       case ACCEPT_PAYMENT:
-        return getCostComponentsForAcceptPayment(dataContextOfAction);
+        return getCostComponentsForAcceptPayment(dataContextOfAction, null);
       case CLOSE:
         return getCostComponentsForClose(dataContextOfAction);
       case MARK_LATE:
@@ -253,7 +257,8 @@
   }
 
   public CostComponentsForRepaymentPeriod getCostComponentsForAcceptPayment(
-      final DataContextOfAction dataContextOfAction)
+      final DataContextOfAction dataContextOfAction,
+      final @Nullable BigDecimal requestedLoanPaymentSize)
   {
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
         = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
@@ -272,11 +277,24 @@
         caseParameters
     );
 
-    final List<ScheduledCharge> scheduledCharges = individualLoanService.getScheduledCharges(
+    final BigDecimal loanPaymentSize;
+    if (requestedLoanPaymentSize != null)
+      loanPaymentSize = requestedLoanPaymentSize;
+    else {
+      final List<ScheduledAction> hypotheticalScheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(
+          today(),
+          caseParameters);
+      final List<ScheduledCharge> hypotheticalScheduledCharges = individualLoanService.getScheduledCharges(
+          productIdentifier,
+          hypotheticalScheduledActions);
+      loanPaymentSize = getLoanPaymentSize(currentBalance, minorCurrencyUnitDigits, hypotheticalScheduledCharges);
+    }
+
+    final List<ScheduledCharge> scheduledChargesForThisAction = individualLoanService.getScheduledCharges(
         productIdentifier,
         Collections.singletonList(scheduledAction));
 
-    final Map<Boolean, List<ScheduledCharge>> chargesSplitIntoScheduledAndAccrued = scheduledCharges.stream()
+    final Map<Boolean, List<ScheduledCharge>> chargesSplitIntoScheduledAndAccrued = scheduledChargesForThisAction.stream()
         .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x, Action.ACCEPT_PAYMENT)));
 
     final Map<ChargeDefinition, CostComponent> accruedCostComponents = chargesSplitIntoScheduledAndAccrued.get(true)
@@ -285,12 +303,14 @@
         .collect(Collectors.toMap(chargeDefinition -> chargeDefinition,
             chargeDefinition -> getAccruedCostComponentToApply(dataContextOfAction, designatorToAccountIdentifierMapper, startOfTerm, chargeDefinition)));
 
+
+
     return getCostComponentsForScheduledCharges(
         accruedCostComponents,
         chargesSplitIntoScheduledAndAccrued.get(false),
         caseParameters.getMaximumBalance(),
         currentBalance,
-        BigDecimal.ZERO,//TODO: This needs to be provided by the user, or calculated.  ZERO is wrong.
+        loanPaymentSize,
         minorCurrencyUnitDigits);
   }
 
@@ -467,6 +487,27 @@
         (chargeDefinition.getAccrualAccountDesignator() != null && chargeDefinition.getAccrualAccountDesignator().equals(accountDesignator));
   }
 
+  static BigDecimal getLoanPaymentSize(final BigDecimal startingBalance,
+                                       final int minorCurrencyUnitDigits,
+                                       final List<ScheduledCharge> scheduledCharges) {
+    final int precision = startingBalance.precision() + minorCurrencyUnitDigits + EXTRA_PRECISION;
+    final Map<Period, BigDecimal> accrualRatesByPeriod
+        = PeriodChargeCalculator.getPeriodAccrualRates(scheduledCharges, precision);
+
+    final int periodCount = accrualRatesByPeriod.size();
+    if (periodCount == 0)
+      return startingBalance;
+
+    final BigDecimal geometricMeanAccrualRate = accrualRatesByPeriod.values().stream()
+        .collect(RateCollectors.geometricMean(precision));
+
+    final MonetaryAmount presentValue = AnnuityPayment.calculate(
+        Money.of(startingBalance, "XXX"),
+        Rate.of(geometricMeanAccrualRate),
+        periodCount);
+    return BigDecimal.valueOf(presentValue.getNumber().doubleValueExact());
+  }
+
   private static LocalDate today() {
     return LocalDate.now(Clock.systemUTC());
   }
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 23eb766..ebdf4cf 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
@@ -23,13 +23,10 @@
 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.javamoney.calc.common.Rate;
-import org.javamoney.moneta.Money;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Nonnull;
-import javax.money.MonetaryAmount;
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.*;
@@ -42,18 +39,14 @@
  */
 @Service
 public class IndividualLoanService {
-  private static final int EXTRA_PRECISION = 4;
   private final ProductService productService;
   private final ChargeDefinitionService chargeDefinitionService;
-  private final PeriodChargeCalculator periodChargeCalculator;
 
   @Autowired
   public IndividualLoanService(final ProductService productService,
-                               final ChargeDefinitionService chargeDefinitionService,
-                               final PeriodChargeCalculator periodChargeCalculator) {
+                               final ChargeDefinitionService chargeDefinitionService) {
     this.productService = productService;
     this.chargeDefinitionService = chargeDefinitionService;
-    this.periodChargeCalculator = periodChargeCalculator;
   }
 
   public PlannedPaymentPage getPlannedPaymentsPage(
@@ -70,16 +63,10 @@
 
     final List<ScheduledCharge> scheduledCharges = getScheduledCharges(productIdentifier, scheduledActions);
 
-    final int precision = caseParameters.getMaximumBalance().precision() + minorCurrencyUnitDigits + EXTRA_PRECISION;
-    final Map<Period, BigDecimal> accrualRatesByPeriod
-        = periodChargeCalculator.getPeriodAccrualRates(scheduledCharges,
-        precision);
-
-    final BigDecimal geometricMeanAccrualRate = accrualRatesByPeriod.values().stream().collect(RateCollectors.geometricMean(precision));
-    final BigDecimal loanPaymentSize = loanPaymentInContextOfAccruedInterest(
+    final BigDecimal loanPaymentSize = CostComponentService.getLoanPaymentSize(
         caseParameters.getMaximumBalance(),
-        accrualRatesByPeriod.size(),
-        geometricMeanAccrualRate);
+        minorCurrencyUnitDigits,
+        scheduledCharges);
 
     final List<PlannedPayment> plannedPaymentsElements = getPlannedPaymentsElements(
         caseParameters.getMaximumBalance(),
@@ -189,17 +176,6 @@
       return scheduledAction.repaymentPeriod;
   }
 
-  private BigDecimal loanPaymentInContextOfAccruedInterest(
-          final BigDecimal initialBalance,
-          final int periodCount,
-          final BigDecimal geometricMeanOfInterest) {
-    if (periodCount == 0)
-      throw new IllegalStateException("To calculate a loan payment there must be at least one payment period.");
-
-    final MonetaryAmount presentValue = AnnuityPayment.calculate(Money.of(initialBalance, "XXX"), Rate.of(geometricMeanOfInterest), periodCount);
-    return BigDecimal.valueOf(presentValue.getNumber().doubleValueExact());
-  }
-
   private List<ScheduledCharge> getScheduledCharges(final List<ScheduledAction> scheduledActions,
                                                     final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByChargeAction,
                                                     final Map<String, List<ChargeDefinition>> chargeDefinitionsMappedByAccrueAction) {
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 d6186a7..f43aa99 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
@@ -34,7 +34,7 @@
   {
   }
 
-  Map<Period, BigDecimal> getPeriodAccrualRates(final List<ScheduledCharge> scheduledCharges, final int precision) {
+  static Map<Period, BigDecimal> getPeriodAccrualRates(final List<ScheduledCharge> scheduledCharges, final int precision) {
     return scheduledCharges.stream()
             .filter(PeriodChargeCalculator::accruedCharge)
             .collect(Collectors.groupingBy(scheduledCharge -> scheduledCharge.getScheduledAction().repaymentPeriod,
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 c5859c5..209dafd 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
@@ -273,7 +273,7 @@
     Mockito.doReturn(chargeDefinitionsByChargeAction).when(chargeDefinitionServiceMock).getChargeDefinitionsMappedByChargeAction(testCase.productIdentifier);
     Mockito.doReturn(chargeDefinitionsByAccrueAction).when(chargeDefinitionServiceMock).getChargeDefinitionsMappedByAccrueAction(testCase.productIdentifier);
 
-    testSubject = new IndividualLoanService(productServiceMock, chargeDefinitionServiceMock, new PeriodChargeCalculator());
+    testSubject = new IndividualLoanService(productServiceMock, chargeDefinitionServiceMock);
   }
 
   @Test