* Fixed regression in loan payment size calculation
* Added comparison to planned payments to loan workflow test.
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/client/IndividualLending.java b/api/src/main/java/io/mifos/individuallending/api/v1/client/IndividualLending.java
index cfafd66..c2264be 100644
--- a/api/src/main/java/io/mifos/individuallending/api/v1/client/IndividualLending.java
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/client/IndividualLending.java
@@ -15,9 +15,10 @@
  */
 package io.mifos.individuallending.api.v1.client;
 
-import io.mifos.portfolio.api.v1.domain.CasePage;
-import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPaymentPage;
 import io.mifos.core.api.util.CustomFeignClientsConfiguration;
+import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPayment;
+import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPaymentPage;
+import io.mifos.portfolio.api.v1.domain.CasePage;
 import org.springframework.cloud.netflix.feign.FeignClient;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -25,6 +26,8 @@
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import java.util.stream.Stream;
+
 /**
  * @author Myrle Krantz
  */
@@ -37,11 +40,32 @@
           produces = MediaType.ALL_VALUE,
           consumes = MediaType.APPLICATION_JSON_VALUE
   )
-  PlannedPaymentPage getPaymentScheduleForCase(@PathVariable("productidentifier") final String productIdentifier,
-                                               @PathVariable("caseidentifier") final String caseIdentifier,
-                                               @RequestParam(value = "pageIndex", required = false) final Integer pageIndex,
-                                               @RequestParam(value = "size", required = false) final Integer size,
-                                               @RequestParam(value = "initialDisbursalDate", required = false) final String initialDisbursalDate);
+  PlannedPaymentPage getPaymentScheduleForCase(
+      @PathVariable("productidentifier") final String productIdentifier,
+      @PathVariable("caseidentifier") final String caseIdentifier,
+      @RequestParam(value = "pageIndex", required = false) final Integer pageIndex,
+      @RequestParam(value = "size", required = false) final Integer size,
+      @RequestParam(value = "initialDisbursalDate", required = false) final String initialDisbursalDate);
+
+  default Stream<PlannedPayment> getPaymentScheduleForCaseStream(
+      final String productIdentifier,
+      final String caseIdentifier,
+      final String initialDisbursalDate) {
+    final PlannedPaymentPage firstPage = this.getPaymentScheduleForCase(
+        productIdentifier,
+        caseIdentifier,
+        0,
+        10,
+        initialDisbursalDate);
+
+    final Integer pageCount = firstPage.getTotalPages();
+        // Sort column is always date and order always ascending so that the order and adjacency of account
+        // entries is always stable. This has the advantage that the set of account entries included in the
+        // stream is set the moment the first call to fetchAccountEntries (above) is made.
+    return Stream.iterate(0, (i) -> i + 1).limit(pageCount)
+        .map(i -> this.getPaymentScheduleForCase(productIdentifier, caseIdentifier, i, 10, initialDisbursalDate))
+        .flatMap(pageI -> pageI.getElements().stream());
+  }
 
   @RequestMapping(
           value = "/individuallending/customers/{customeridentifier}/cases",
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 2aedaa3..968e9da 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
@@ -267,24 +267,29 @@
     Assert.assertEquals(actionList, portfolioManager.getActionsForCase(productIdentifier, customerCaseIdentifier));
   }
 
-  void checkCostComponentForActionCorrect(final String productIdentifier,
-                                          final String customerCaseIdentifier,
-                                          final Action action,
-                                          final Set<String> accountDesignators,
-                                          final BigDecimal amount,
-                                          final CostComponent... expectedCostComponents) {
+  Payment checkCostComponentForActionCorrect(
+      final String productIdentifier,
+      final String customerCaseIdentifier,
+      final Action action,
+      final Set<String> accountDesignators,
+      final BigDecimal amount,
+      final LocalDateTime forDateTime,
+      final CostComponent... expectedCostComponents) {
     final Payment payment = portfolioManager.getCostComponentsForAction(
         productIdentifier,
         customerCaseIdentifier,
         action.name(),
         accountDesignators,
-        amount
+        amount,
+        DateConverter.toIsoString(forDateTime)
     );
     final Set<CostComponent> setOfCostComponents = new HashSet<>(payment.getCostComponents());
     final Set<CostComponent> setOfExpectedCostComponents = Stream.of(expectedCostComponents)
         .filter(x -> x.getAmount().compareTo(BigDecimal.ZERO) != 0)
         .collect(Collectors.toSet());
     Assert.assertEquals(setOfExpectedCostComponents, setOfCostComponents);
+
+    return payment;
   }
 
   void setFeeToFixedValue(final String productIdentifier,
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 bf356e0..0ff31b7 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
@@ -22,6 +22,7 @@
 import io.mifos.core.api.util.ApiFactory;
 import io.mifos.core.lang.DateConverter;
 import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
+import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPayment;
 import io.mifos.individuallending.api.v1.domain.product.AccountDesignators;
 import io.mifos.individuallending.api.v1.domain.product.ChargeIdentifiers;
 import io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator;
@@ -46,6 +47,7 @@
 import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
 import static io.mifos.portfolio.Fixture.MINOR_CURRENCY_UNIT_DIGITS;
@@ -54,8 +56,8 @@
  * @author Myrle Krantz
  */
 public class TestAccountingInteractionInLoanWorkflow extends AbstractPortfolioTest {
-  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 PROCESSING_FEE_AMOUNT = BigDecimal.valueOf(50_00, MINOR_CURRENCY_UNIT_DIGITS);
+  private static final BigDecimal LOAN_ORIGINATION_FEE_AMOUNT = BigDecimal.valueOf(50_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";
@@ -85,10 +87,11 @@
 
   @Test
   public void workflowTerminatingInApplicationDenial() throws InterruptedException {
+    final LocalDateTime today = midnightToday();
     step1CreateProduct();
     step2CreateCase();
-    step3OpenCase();
-    step4DenyCase();
+    step3OpenCase(today);
+    step4DenyCase(today);
   }
 
   @Test
@@ -97,18 +100,18 @@
 
     step1CreateProduct();
     step2CreateCase();
-    step3OpenCase();
-    step4ApproveCase();
+    step3OpenCase(today);
+    step4ApproveCase(today);
     step5Disburse(
         BigDecimal.valueOf(2_000_00, MINOR_CURRENCY_UNIT_DIGITS),
+        today,
         UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(20_00, MINOR_CURRENCY_UNIT_DIGITS));
     step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), BigDecimal.ZERO);
     step7PaybackPartialAmount(
         expectedCurrentPrincipal.add(nonLateFees).add(interestAccrued),
         today,
-        0,
         BigDecimal.ZERO);
-    step8Close();
+    step8Close(today);
   }
 
   @Test
@@ -117,21 +120,22 @@
 
     step1CreateProduct();
     step2CreateCase();
-    step3OpenCase();
-    step4ApproveCase();
+    step3OpenCase(today);
+    step4ApproveCase(today);
     step5Disburse(
         BigDecimal.valueOf(500_00, MINOR_CURRENCY_UNIT_DIGITS),
+        today,
         ChargeIdentifiers.DISBURSEMENT_FEE_ID, BigDecimal.valueOf(10_00, MINOR_CURRENCY_UNIT_DIGITS));
     step5Disburse(
         BigDecimal.valueOf(1_500_00, MINOR_CURRENCY_UNIT_DIGITS),
+        today,
         UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(15_00, MINOR_CURRENCY_UNIT_DIGITS));
     step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), BigDecimal.ZERO);
     step7PaybackPartialAmount(
         expectedCurrentPrincipal.add(nonLateFees).add(interestAccrued),
         today,
-        0,
         BigDecimal.ZERO);
-    step8Close();
+    step8Close(today);
   }
 
   @Test
@@ -140,29 +144,33 @@
 
     step1CreateProduct();
     step2CreateCase();
-    step3OpenCase();
-    step4ApproveCase();
+    step3OpenCase(today);
+    step4ApproveCase(today);
     step5Disburse(
         BigDecimal.valueOf(2_000_00, MINOR_CURRENCY_UNIT_DIGITS),
+        today,
         UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(20_00, MINOR_CURRENCY_UNIT_DIGITS));
     step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), BigDecimal.ZERO);
     final BigDecimal repayment1 = expectedCurrentPrincipal.divide(BigDecimal.valueOf(2), BigDecimal.ROUND_HALF_EVEN);
     step7PaybackPartialAmount(
         repayment1.setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN),
         today,
-        0, BigDecimal.ZERO);
-    step7PaybackPartialAmount(expectedCurrentPrincipal, today, 0, BigDecimal.ZERO);
-    step8Close();
+        BigDecimal.ZERO);
+    step7PaybackPartialAmount(expectedCurrentPrincipal, today, BigDecimal.ZERO);
+    step8Close(today);
   }
 
   @Test
   public void workflowWithNegativePaymentSize() throws InterruptedException {
+    final LocalDateTime today = midnightToday();
+
     step1CreateProduct();
     step2CreateCase();
-    step3OpenCase();
-    step4ApproveCase();
+    step3OpenCase(today);
+    step4ApproveCase(today);
     try {
       step5Disburse(BigDecimal.valueOf(-2).setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN),
+          today,
           UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.ZERO.setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN));
       Assert.fail("Expected an IllegalArgumentException.");
     }
@@ -175,38 +183,33 @@
 
     step1CreateProduct();
     step2CreateCase();
-    step3OpenCase();
-    step4ApproveCase();
+    step3OpenCase(today);
+    step4ApproveCase(today);
+
+    final List<PlannedPayment> plannedPayments = individualLending.getPaymentScheduleForCaseStream(
+        product.getIdentifier(),
+        customerCase.getIdentifier(),
+        null)
+        .collect(Collectors.toList());
+
     step5Disburse(
         BigDecimal.valueOf(2_000_00, MINOR_CURRENCY_UNIT_DIGITS),
+        today,
         UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(20_00, MINOR_CURRENCY_UNIT_DIGITS));
 
     int week = 0;
-    final List<BigDecimal> repayments = new ArrayList<>();
     while (expectedCurrentPrincipal.compareTo(BigDecimal.ZERO) > 0) {
-      logger.info("Simulating week {}. Expected current balance {}.", week, expectedCurrentPrincipal);
+      logger.info("Simulating week {}. Expected current principal {}.", week, expectedCurrentPrincipal);
       step6CalculateInterestAndCheckForLatenessForWeek(today, week);
-      final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week+1)*7);
-      repayments.add(nextRepaymentAmount);
-      step7PaybackPartialAmount(nextRepaymentAmount, today, (week+1)*7, BigDecimal.ZERO);
+      final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today.plusDays((week+1)*7));
+      final Payment payment = step7PaybackPartialAmount(nextRepaymentAmount, today.plusDays((week + 1) * 7), BigDecimal.ZERO);
+      payment.getBalanceAdjustments().remove(AccountDesignators.INTEREST_ACCRUAL);
+      payment.getBalanceAdjustments().remove(AccountDesignators.LATE_FEE_ACCRUAL); //Remove accrual accounts for comparison.
+      Assert.assertEquals(plannedPayments.get(week+1).getPayment(), payment);
       week++;
     }
 
-    checkRepaymentVariance(repayments);
-
-
-    step8Close();
-  }
-
-  private void checkRepaymentVariance(List<BigDecimal> repayments) {
-    final Set<BigDecimal> setOfRepayments = new HashSet<>(repayments);
-    Assert.assertTrue("Too many payment sizes. Payments are " + repayments,
-        + setOfRepayments.size() <= 2); //There should be a standard payment size with only one slightly off.
-    final BigDecimal minPayment = setOfRepayments.stream().min(BigDecimal::compareTo).orElseThrow(IllegalStateException::new);
-    final BigDecimal maxPayment = setOfRepayments.stream().max(BigDecimal::compareTo).orElseThrow(IllegalStateException::new);
-    final BigDecimal delta = maxPayment.subtract(minPayment).abs();
-    Assert.assertTrue("Payments vary too much. Payments are " + repayments,
-        delta.divide(maxPayment, BigDecimal.ROUND_HALF_EVEN).compareTo(BigDecimal.valueOf(0.01)) <= 0);
+    step8Close(DateConverter.fromIsoString(plannedPayments.get(plannedPayments.size()-1).getPayment().getDate()));
   }
 
   @Test
@@ -215,55 +218,58 @@
 
     step1CreateProduct();
     step2CreateCase();
-    step3OpenCase();
-    step4ApproveCase();
+    step3OpenCase(today);
+    step4ApproveCase(today);
+
+    final List<PlannedPayment> plannedPayments = individualLending.getPaymentScheduleForCaseStream(
+        product.getIdentifier(),
+        customerCase.getIdentifier(),
+        null)
+        .collect(Collectors.toList());
+
     step5Disburse(
         BigDecimal.valueOf(2_000_00, MINOR_CURRENCY_UNIT_DIGITS),
+        today,
         UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(20_00, MINOR_CURRENCY_UNIT_DIGITS));
 
     int week = 0;
     final int weekOfLateRepayment = 3;
-    final List<BigDecimal> repayments = new ArrayList<>();
     while (expectedCurrentPrincipal.compareTo(BigDecimal.ZERO) > 0) {
       logger.info("Simulating week {}. Expected current balance {}.", week, expectedCurrentPrincipal);
       if (week == weekOfLateRepayment) {
-        final BigDecimal lateFee = BigDecimal.valueOf(17_31, MINOR_CURRENCY_UNIT_DIGITS); //??? TODO: check the late fee value.
+        final BigDecimal lateFee = BigDecimal.valueOf(16_53, MINOR_CURRENCY_UNIT_DIGITS); //??? TODO: check the late fee value.
         step6CalculateInterestAndCheckForLatenessForRangeOfDays(
             today,
             (week * 7) + 1,
             (week + 1) * 7 + 2,
             8,
             lateFee);
-        final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week + 1) * 7 + 2);
-        repayments.add(nextRepaymentAmount);
-        step7PaybackPartialAmount(nextRepaymentAmount, today, (week + 1) * 7 + 2, lateFee);
+        final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today.plusDays((week + 1) * 7 + 2));
+        step7PaybackPartialAmount(nextRepaymentAmount, today.plusDays((week + 1) * 7 + 2), lateFee);
       }
       else {
         step6CalculateInterestAndCheckForLatenessForWeek(today, week);
-        final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week + 1) * 7);
-        repayments.add(nextRepaymentAmount);
-        step7PaybackPartialAmount(nextRepaymentAmount, today, (week + 1) * 7, BigDecimal.ZERO);
+        final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today.plusDays((week + 1) * 7));
+        final Payment payment = step7PaybackPartialAmount(nextRepaymentAmount, today.plusDays((week + 1) * 7), BigDecimal.ZERO);
+        payment.getBalanceAdjustments().remove(AccountDesignators.INTEREST_ACCRUAL);
+        payment.getBalanceAdjustments().remove(AccountDesignators.CUSTOMER_LOAN_INTEREST); //Remove accrual accounting components for comparison, since planned payments are done without accrual accounting.
+        Assert.assertEquals(plannedPayments.get(week+1).getPayment(), payment);
       }
       week++;
     }
 
-    repayments.remove(3);
-
-    checkRepaymentVariance(repayments);
-
-    step8Close();
+    step8Close(DateConverter.fromIsoString(plannedPayments.get(plannedPayments.size()-1).getPayment().getDate()));
   }
 
   private BigDecimal findNextRepaymentAmount(
-      final LocalDateTime referenceDate,
-      final int dayNumber) {
+      final LocalDateTime forDateTime) {
     final Payment nextPayment = portfolioManager.getCostComponentsForAction(
         product.getIdentifier(),
         customerCase.getIdentifier(),
         Action.ACCEPT_PAYMENT.name(),
         null,
         null,
-        DateConverter.toIsoString(referenceDate.plusDays(dayNumber)));
+        DateConverter.toIsoString(forDateTime));
     final BigDecimal nextRepaymentAmount = nextPayment.getBalanceAdjustments()
         .getOrDefault(AccountDesignators.ENTRY, BigDecimal.ZERO).negate();
     Assert.assertTrue(nextRepaymentAmount.signum() != -1);
@@ -336,14 +342,15 @@
   }
 
   //Open the case and accept a processing fee.
-  private void step3OpenCase() throws InterruptedException {
+  private void step3OpenCase(final LocalDateTime forDateTime) throws InterruptedException {
     logger.info("step3OpenCase");
     checkCostComponentForActionCorrect(
         product.getIdentifier(),
         customerCase.getIdentifier(),
         Action.OPEN,
         null,
-        null);
+        null,
+        forDateTime);
     checkStateTransfer(
         product.getIdentifier(),
         customerCase.getIdentifier(),
@@ -356,14 +363,14 @@
 
 
   //Deny the case. Once this is done, no more actions are possible for the case.
-  private void step4DenyCase() throws InterruptedException {
+  private void step4DenyCase(final LocalDateTime forDateTime) throws InterruptedException {
     logger.info("step4DenyCase");
     checkCostComponentForActionCorrect(
         product.getIdentifier(),
         customerCase.getIdentifier(),
         Action.DENY,
         null,
-        null);
+        null, forDateTime);
     checkStateTransfer(
         product.getIdentifier(),
         customerCase.getIdentifier(),
@@ -376,7 +383,8 @@
 
 
   //Approve the case, accept a loan origination fee, and prepare to disburse the loan by earmarking the funds.
-  private void step4ApproveCase() throws InterruptedException {
+  private void step4ApproveCase(final LocalDateTime forDateTime) throws InterruptedException
+  {
     logger.info("step4ApproveCase");
 
     markTaskExecuted(product, customerCase, taskDefinition);
@@ -386,7 +394,7 @@
         customerCase.getIdentifier(),
         Action.APPROVE,
         null,
-        null);
+        null, forDateTime);
     checkStateTransfer(
         product.getIdentifier(),
         customerCase.getIdentifier(),
@@ -418,6 +426,7 @@
   //Approve the case, accept a loan origination fee, and prepare to disburse the loan by earmarking the funds.
   private void step5Disburse(
       final BigDecimal amount,
+      final LocalDateTime forDateTime,
       final String whichDisbursementFee,
       final BigDecimal disbursementFeeAmount) throws InterruptedException {
     logger.info("step5Disburse  '{}'", amount);
@@ -427,6 +436,7 @@
         Action.DISBURSE,
         Sets.newLinkedHashSet(AccountDesignators.ENTRY, AccountDesignators.CUSTOMER_LOAN_GROUP),
         amount,
+        forDateTime,
         new CostComponent(whichDisbursementFee, disbursementFeeAmount),
         new CostComponent(ChargeIdentifiers.LOAN_ORIGINATION_FEE_ID, LOAN_ORIGINATION_FEE_AMOUNT),
         new CostComponent(ChargeIdentifiers.PROCESSING_FEE_ID, PROCESSING_FEE_AMOUNT),
@@ -459,7 +469,7 @@
 
     expectedCurrentPrincipal = expectedCurrentPrincipal.add(amount);
     interestAccrued = BigDecimal.ZERO;
-    nonLateFees = nonLateFees.add(disbursementFeeAmount);
+    nonLateFees = nonLateFees.add(disbursementFeeAmount).add(PROCESSING_FEE_AMOUNT).add(LOAN_ORIGINATION_FEE_AMOUNT);
     lateFees = BigDecimal.ZERO;
 
     updateBalanceMock();
@@ -510,11 +520,11 @@
 
   //Perform daily interest calculation.
   private void step6CalculateInterestAccrualAndCheckForLateness(
-      final LocalDateTime forTime,
+      final LocalDateTime forDateTime,
       final BigDecimal calculatedLateFee) throws InterruptedException {
-    logger.info("step6CalculateInterestAccrualAndCheckForLateness  '{}'", forTime);
+    logger.info("step6CalculateInterestAccrualAndCheckForLateness  '{}'", forDateTime);
     final String beatIdentifier = "alignment0";
-    final String midnightTimeStamp = DateConverter.toIsoString(forTime);
+    final String midnightTimeStamp = DateConverter.toIsoString(forDateTime);
 
     final BigDecimal dailyInterestRate = Fixture.INTEREST_RATE
         .divide(BigDecimal.valueOf(100), 8, BigDecimal.ROUND_HALF_EVEN)
@@ -535,6 +545,7 @@
         Action.APPLY_INTEREST,
         null,
         null,
+        forDateTime,
         new CostComponent(ChargeIdentifiers.INTEREST_ID, calculatedInterest));
 
     if (calculatedLateFee.compareTo(BigDecimal.ZERO) != 0) {
@@ -544,6 +555,7 @@
           Action.MARK_LATE,
           null,
           null,
+          forDateTime,
           new CostComponent(ChargeIdentifiers.LATE_FEE_ID, calculatedLateFee));
     }
     final BeatPublish interestBeat = new BeatPublish(beatIdentifier, midnightTimeStamp);
@@ -603,20 +615,20 @@
     logger.info("Completed step6CalculateInterestAccrualAndCheckForLateness");
   }
 
-  private void step7PaybackPartialAmount(
+  private Payment step7PaybackPartialAmount(
       final BigDecimal amount,
-      final LocalDateTime referenceDate,
-      final int dayNumber,
+      final LocalDateTime forDateTime,
       final BigDecimal lateFee) throws InterruptedException {
-    logger.info("step7PaybackPartialAmount '{}'", amount);
+    logger.info("step7PaybackPartialAmount '{}' '{}'", amount, forDateTime);
     final BigDecimal principal = amount.subtract(interestAccrued).subtract(lateFee.add(nonLateFees));
 
-    checkCostComponentForActionCorrect(
+    final Payment payment = checkCostComponentForActionCorrect(
         product.getIdentifier(),
         customerCase.getIdentifier(),
         Action.ACCEPT_PAYMENT,
         new HashSet<>(Arrays.asList(AccountDesignators.ENTRY, AccountDesignators.CUSTOMER_LOAN_GROUP, AccountDesignators.LOAN_FUNDS_SOURCE)),
         amount,
+        forDateTime,
         new CostComponent(ChargeIdentifiers.REPAY_PRINCIPAL_ID, principal),
         new CostComponent(ChargeIdentifiers.REPAY_INTEREST_ID, interestAccrued),
         new CostComponent(ChargeIdentifiers.REPAY_FEES_ID, lateFee.add(nonLateFees)),
@@ -626,7 +638,7 @@
         product.getIdentifier(),
         customerCase.getIdentifier(),
         Action.ACCEPT_PAYMENT,
-        referenceDate.plusDays(dayNumber),
+        forDateTime,
         Collections.singletonList(assignEntryToTeller()),
         amount,
         IndividualLoanEventConstants.ACCEPT_PAYMENT_INDIVIDUALLOAN_CASE,
@@ -670,9 +682,12 @@
 
     updateBalanceMock();
     logger.info("Completed step7PaybackPartialAmount");
+    return payment;
   }
 
-  private void step8Close() throws InterruptedException {
+  private void step8Close(
+      final LocalDateTime forDateTime) throws InterruptedException
+  {
     logger.info("step8Close");
 
     checkCostComponentForActionCorrect(
@@ -680,7 +695,8 @@
         customerCase.getIdentifier(),
         Action.CLOSE,
         null,
-        null);
+        null,
+        forDateTime);
     checkStateTransfer(
         product.getIdentifier(),
         customerCase.getIdentifier(),
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 028fec6..d4f9246 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -482,7 +482,7 @@
         forDate,
         runningBalances);
 
-    return paymentBuilder.buildPayment(action, forAccountDesignators);
+    return paymentBuilder.buildPayment(action, forAccountDesignators, forDate);
   }
 
   public static void checkActionCanBeExecuted(final Case.State state, final Action action) {
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java b/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
index f5110cb..eab188d 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
@@ -320,13 +320,10 @@
       customerCase.setCurrentState(Case.State.ACTIVE.name());
       caseRepository.save(customerCase);
     }
-    final String customerLoanPrinicipalAccountIdentifier = designatorToAccountIdentifierMapper.mapOrThrow(AccountDesignators.CUSTOMER_LOAN_PRINCIPAL);
-    final String customerLoanInterestAccountIdentifier = designatorToAccountIdentifierMapper.mapOrThrow(AccountDesignators.CUSTOMER_LOAN_INTEREST);
-    final String customerLoanFeesAccountIdentifier = designatorToAccountIdentifierMapper.mapOrThrow(AccountDesignators.CUSTOMER_LOAN_FEES);
-    final BigDecimal currentBalance = accountingAdapter.getTotalOfCurrentAccountBalances(customerLoanPrinicipalAccountIdentifier, customerLoanInterestAccountIdentifier, customerLoanFeesAccountIdentifier);
+    final BigDecimal currentBalance = runningBalances.getBalance(AccountDesignators.CUSTOMER_LOAN_GROUP);
 
     final BigDecimal newLoanPaymentSize = disbursePaymentBuilderService.getLoanPaymentSizeForSingleDisbursement(
-        currentBalance.add(disbursalAmount),
+        currentBalance.add(paymentBuilder.getBalanceAdjustment(AccountDesignators.ENTRY)),
         dataContextOfAction);
 
     dataContextOfAction.getCaseParametersEntity().setPaymentSize(newLoanPaymentSize);
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 744e82e..4d6521b 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
@@ -165,7 +165,7 @@
                   minorCurrencyUnitDigits,
                   false);
 
-      plannedPayments.add(paymentBuilder.accumulatePlannedPayment(balances));
+      plannedPayments.add(paymentBuilder.accumulatePlannedPayment(balances, repaymentPeriod.getEndDate()));
     }
     return plannedPayments;
   }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderService.java
index 2b989e9..d10f130 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderService.java
@@ -88,24 +88,15 @@
     final BigDecimal loanPaymentSize;
 
     if (requestedLoanPaymentSize != null) {
-      loanPaymentSize = requestedLoanPaymentSize;
+      loanPaymentSize = requestedLoanPaymentSize
+          .min(runningBalances.getBalance(AccountDesignators.CUSTOMER_LOAN_GROUP));
+    }
+    else if (scheduledAction.getActionPeriod() != null && scheduledAction.getActionPeriod().isLastPeriod()) {
+      loanPaymentSize = runningBalances.getBalance(AccountDesignators.CUSTOMER_LOAN_GROUP);
     }
     else {
-      if (scheduledAction.getActionPeriod() != null && scheduledAction.getActionPeriod().isLastPeriod()) {
-        loanPaymentSize = runningBalances.getBalance(AccountDesignators.CUSTOMER_LOAN_GROUP);
-      }
-      else {
-        final BigDecimal paymentSizeBeforeOnTopCharges = runningBalances.getBalance(AccountDesignators.CUSTOMER_LOAN_GROUP)
-            .min(dataContextOfAction.getCaseParametersEntity().getPaymentSize());
-
-        @SuppressWarnings("UnnecessaryLocalVariable")
-        final BigDecimal paymentSizeIncludingOnTopCharges = accruedCostComponents.entrySet().stream()
-            .filter(entry -> entry.getKey().getChargeOnTop() != null && entry.getKey().getChargeOnTop())
-            .map(entry -> entry.getValue().getAmount())
-            .reduce(paymentSizeBeforeOnTopCharges, BigDecimal::add);
-
-        loanPaymentSize = paymentSizeIncludingOnTopCharges;
-      }
+      loanPaymentSize = dataContextOfAction.getCaseParametersEntity().getPaymentSize()
+          .min(runningBalances.getBalance(AccountDesignators.CUSTOMER_LOAN_GROUP));
     }
 
 
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilder.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilder.java
index 255c6cf..d85056a 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilder.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilder.java
@@ -16,6 +16,7 @@
 package io.mifos.individuallending.internal.service.costcomponent;
 
 import com.google.common.collect.Sets;
+import io.mifos.core.lang.DateConverter;
 import io.mifos.individuallending.IndividualLendingPatternFactory;
 import io.mifos.individuallending.api.v1.domain.caseinstance.PlannedPayment;
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
@@ -26,7 +27,9 @@
 import io.mifos.portfolio.api.v1.domain.RequiredAccountAssignment;
 import io.mifos.portfolio.service.internal.util.ChargeInstance;
 
+import javax.annotation.Nullable;
 import java.math.BigDecimal;
+import java.time.LocalDate;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -48,8 +51,17 @@
     this.accrualAccounting = accrualAccounting;
   }
 
-  public Payment buildPayment(final Action action, final Set<String> forAccountDesignators) {
+  private Map<String, BigDecimal> copyBalanceAdjustments() {
+    return balanceAdjustments.entrySet().stream()
+        .filter(x -> x.getValue().compareTo(BigDecimal.ZERO) != 0)
+        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+  }
 
+  public Payment buildPayment(
+      final Action action,
+      final Set<String> forAccountDesignators,
+      final @Nullable LocalDate forDate)
+  {
     if (!forAccountDesignators.isEmpty()) {
       final Stream<Map.Entry<ChargeDefinition, CostComponent>> costComponentStream = stream()
           .filter(costComponentEntry -> chargeReferencesAccountDesignators(
@@ -63,15 +75,17 @@
               costComponentEntry.getValue().getAmount()))
           .collect(Collectors.toList());
 
-      return new Payment(costComponentList, balanceAdjustments);
+      final Payment ret = new Payment(costComponentList, copyBalanceAdjustments());
+      ret.setDate(forDate == null ? null : DateConverter.toIsoString(forDate.atStartOfDay()));
+      return ret;
     }
     else {
-      return buildPayment();
+      return buildPayment(forDate);
     }
 
   }
 
-  private Payment buildPayment() {
+  private Payment buildPayment(final @Nullable LocalDate forDate) {
     final Stream<Map.Entry<ChargeDefinition, CostComponent>> costComponentStream  = stream();
 
     final List<CostComponent> costComponentList = costComponentStream
@@ -80,11 +94,15 @@
             costComponentEntry.getValue().getAmount()))
         .collect(Collectors.toList());
 
-    return new Payment(costComponentList, balanceAdjustments);
+    final Payment ret = new Payment(costComponentList, copyBalanceAdjustments());
+    ret.setDate(forDate == null ? null : DateConverter.toIsoString(forDate.atStartOfDay()));
+    return ret;
   }
 
-  public PlannedPayment accumulatePlannedPayment(final SimulatedRunningBalances balances) {
-    final Payment payment = buildPayment();
+  public PlannedPayment accumulatePlannedPayment(
+      final SimulatedRunningBalances balances,
+      final @Nullable LocalDate forDate) {
+    final Payment payment = buildPayment(forDate);
     balanceAdjustments.forEach(balances::adjustBalance);
     final Map<String, BigDecimal> balancesCopy = balances.snapshot();
 
@@ -101,7 +119,7 @@
         .collect(Collectors.toList());
   }
 
-  BigDecimal getBalanceAdjustment(final String... accountDesignators) {
+  public BigDecimal getBalanceAdjustment(final String... accountDesignators) {
     return Arrays.stream(accountDesignators)
         .map(accountDesignator -> balanceAdjustments.getOrDefault(accountDesignator, BigDecimal.ZERO))
         .reduce(BigDecimal.ZERO, BigDecimal::add);
@@ -154,7 +172,7 @@
     return chargeDefinition.getAccrualAccountDesignator() != null;
   }
 
-  void addToBalance(
+  private void addToBalance(
       final String accountDesignator,
       final BigDecimal chargeAmount) {
     final BigDecimal currentAdjustment = balanceAdjustments.getOrDefault(accountDesignator, BigDecimal.ZERO);
@@ -162,7 +180,7 @@
     balanceAdjustments.put(accountDesignator, newAdjustment);
   }
 
-  void addToCostComponent(
+  private void addToCostComponent(
       final ChargeDefinition chargeDefinition,
       final BigDecimal amount) {
     final CostComponent costComponent = costComponents
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/schedule/Period.java b/service/src/main/java/io/mifos/individuallending/internal/service/schedule/Period.java
index 0a0532b..db0e069 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/schedule/Period.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/schedule/Period.java
@@ -57,7 +57,7 @@
     return beginDate;
   }
 
-  LocalDate getEndDate() {
+  public LocalDate getEndDate() {
     return endDate;
   }
 
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderServiceTest.java b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderServiceTest.java
index 528270c..b5c8134 100644
--- a/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderServiceTest.java
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderServiceTest.java
@@ -23,7 +23,7 @@
     final PaymentBuilder paymentBuilder = PaymentBuilderServiceTestHarness.constructCallToPaymentBuilder(
         AcceptPaymentBuilderService::new, testCase);
 
-    final Payment payment = paymentBuilder.buildPayment(Action.ACCEPT_PAYMENT, Collections.emptySet());
+    final Payment payment = paymentBuilder.buildPayment(Action.ACCEPT_PAYMENT, Collections.emptySet(), testCase.forDate);
     Assert.assertNotNull(payment);
     final Map<String, CostComponent> mappedCostComponents = payment.getCostComponents().stream()
         .collect(Collectors.toMap(CostComponent::getChargeIdentifier, x -> x));
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderServiceTest.java b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderServiceTest.java
index 5b56481..cc2a384 100644
--- a/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderServiceTest.java
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderServiceTest.java
@@ -22,7 +22,7 @@
     final PaymentBuilder paymentBuilder = PaymentBuilderServiceTestHarness.constructCallToPaymentBuilder(
         ApplyInterestPaymentBuilderService::new, testCase);
 
-    final Payment payment = paymentBuilder.buildPayment(Action.APPLY_INTEREST, Collections.emptySet());
+    final Payment payment = paymentBuilder.buildPayment(Action.APPLY_INTEREST, Collections.emptySet(), testCase.forDate);
     Assert.assertNotNull(payment);
     final Map<String, CostComponent> mappedCostComponents = payment.getCostComponents().stream()
         .collect(Collectors.toMap(CostComponent::getChargeIdentifier, x -> x));