Preparing interface changes for late fee checking:
* added datetime to command event.
* added payment size column to case parameters.
* added/adjusted properties to set time to check for lateness (next to interest)
* added command for lateness checking.
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParameters.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParameters.java
index 5fc2e9c..7be3e81 100644
--- a/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParameters.java
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/CaseParameters.java
@@ -50,6 +50,9 @@
   @Valid
   private PaymentCycle paymentCycle;
 
+  @Range(min = 0)
+  private BigDecimal paymentSize;
+
   public CaseParameters() {
   }
 
@@ -97,31 +100,41 @@
     this.paymentCycle = paymentCycle;
   }
 
+  public BigDecimal getPaymentSize() {
+    return paymentSize;
+  }
+
+  public void setPaymentSize(BigDecimal paymentSize) {
+    this.paymentSize = paymentSize;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
     CaseParameters that = (CaseParameters) o;
     return Objects.equals(customerIdentifier, that.customerIdentifier) &&
-            Objects.equals(creditWorthinessSnapshots, that.creditWorthinessSnapshots) &&
-            Objects.equals(maximumBalance, that.maximumBalance) &&
-            Objects.equals(termRange, that.termRange) &&
-            Objects.equals(paymentCycle, that.paymentCycle);
+        Objects.equals(creditWorthinessSnapshots, that.creditWorthinessSnapshots) &&
+        Objects.equals(maximumBalance, that.maximumBalance) &&
+        Objects.equals(termRange, that.termRange) &&
+        Objects.equals(paymentCycle, that.paymentCycle) &&
+        Objects.equals(paymentSize, that.paymentSize);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(customerIdentifier, creditWorthinessSnapshots, maximumBalance, termRange, paymentCycle);
+    return Objects.hash(customerIdentifier, creditWorthinessSnapshots, maximumBalance, termRange, paymentCycle, paymentSize);
   }
 
   @Override
   public String toString() {
     return "CaseParameters{" +
-            "customerIdentifier='" + customerIdentifier + '\'' +
-            ", creditWorthinessSnapshots=" + creditWorthinessSnapshots +
-            ", maximumBalance=" + maximumBalance +
-            ", termRange=" + termRange +
-            ", paymentCycle=" + paymentCycle +
-            '}';
+        "customerIdentifier='" + customerIdentifier + '\'' +
+        ", creditWorthinessSnapshots=" + creditWorthinessSnapshots +
+        ", maximumBalance=" + maximumBalance +
+        ", termRange=" + termRange +
+        ", paymentCycle=" + paymentCycle +
+        ", paymentSize=" + paymentSize +
+        '}';
   }
 }
\ No newline at end of file
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/events/IndividualLoanCommandEvent.java b/api/src/main/java/io/mifos/individuallending/api/v1/events/IndividualLoanCommandEvent.java
index 2ae1249..4576d73 100644
--- a/api/src/main/java/io/mifos/individuallending/api/v1/events/IndividualLoanCommandEvent.java
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/events/IndividualLoanCommandEvent.java
@@ -24,13 +24,15 @@
 public class IndividualLoanCommandEvent {
   private String productIdentifier;
   private String caseIdentifier;
+  private String forDate;
 
   public IndividualLoanCommandEvent() {
   }
 
-  public IndividualLoanCommandEvent(String productIdentifier, String caseIdentifier) {
+  public IndividualLoanCommandEvent(String productIdentifier, String caseIdentifier, String forDate) {
     this.productIdentifier = productIdentifier;
     this.caseIdentifier = caseIdentifier;
+    this.forDate = forDate;
   }
 
   public String getProductIdentifier() {
@@ -49,25 +51,35 @@
     this.caseIdentifier = caseIdentifier;
   }
 
+  public String getForDate() {
+    return forDate;
+  }
+
+  public void setForDate(String forDate) {
+    this.forDate = forDate;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
     IndividualLoanCommandEvent that = (IndividualLoanCommandEvent) o;
     return Objects.equals(productIdentifier, that.productIdentifier) &&
-            Objects.equals(caseIdentifier, that.caseIdentifier);
+        Objects.equals(caseIdentifier, that.caseIdentifier) &&
+        Objects.equals(forDate, that.forDate);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(productIdentifier, caseIdentifier);
+    return Objects.hash(productIdentifier, caseIdentifier, forDate);
   }
 
   @Override
   public String toString() {
     return "IndividualLoanCommandEvent{" +
-            "productIdentifier='" + productIdentifier + '\'' +
-            ", caseIdentifier='" + caseIdentifier + '\'' +
-            '}';
+        "productIdentifier='" + productIdentifier + '\'' +
+        ", caseIdentifier='" + caseIdentifier + '\'' +
+        ", forDate='" + forDate + '\'' +
+        '}';
   }
 }
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/events/IndividualLoanEventConstants.java b/api/src/main/java/io/mifos/individuallending/api/v1/events/IndividualLoanEventConstants.java
index c5fa4f2..89e1e4f 100644
--- a/api/src/main/java/io/mifos/individuallending/api/v1/events/IndividualLoanEventConstants.java
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/events/IndividualLoanEventConstants.java
@@ -28,6 +28,7 @@
   String DISBURSE_INDIVIDUALLOAN_CASE = "disburse-individualloan-case";
   String APPLY_INTEREST_INDIVIDUALLOAN_CASE = "apply-interest-individualloan-case";
   String ACCEPT_PAYMENT_INDIVIDUALLOAN_CASE = "accept-payment-individualloan-case";
+  String CHECK_LATE_INDIVIDUALLOAN_CASE = "check-late-individualloan-case";
   String MARK_LATE_INDIVIDUALLOAN_CASE = "mark-late-individualloan-case";
   String WRITE_OFF_INDIVIDUALLOAN_CASE = "write-off-individualloan-case";
   String CLOSE_INDIVIDUALLOAN_CASE = "close-individualloan-case";
@@ -39,6 +40,7 @@
   String SELECTOR_DISBURSE_INDIVIDUALLOAN_CASE = SELECTOR_NAME + " = '" + DISBURSE_INDIVIDUALLOAN_CASE + "'";
   String SELECTOR_APPLY_INTEREST_INDIVIDUALLOAN_CASE = SELECTOR_NAME + " = '" + APPLY_INTEREST_INDIVIDUALLOAN_CASE + "'";
   String SELECTOR_ACCEPT_PAYMENT_INDIVIDUALLOAN_CASE = SELECTOR_NAME + " = '" + ACCEPT_PAYMENT_INDIVIDUALLOAN_CASE + "'";
+  String SELECTOR_CHECK_LATE_INDIVIDUALLOAN_CASE = SELECTOR_NAME + " = '" + CHECK_LATE_INDIVIDUALLOAN_CASE + "'";
   String SELECTOR_MARK_LATE_INDIVIDUALLOAN_CASE = SELECTOR_NAME + " = '" + MARK_LATE_INDIVIDUALLOAN_CASE + "'";
   String SELECTOR_WRITE_OFF_INDIVIDUALLOAN_CASE = SELECTOR_NAME + " = '" + WRITE_OFF_INDIVIDUALLOAN_CASE + "'";
   String SELECTOR_CLOSE_INDIVIDUALLOAN_CASE = SELECTOR_NAME + " = '" + CLOSE_INDIVIDUALLOAN_CASE + "'";
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 4c7dfcd..62de9e5 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
@@ -18,6 +18,7 @@
 import io.mifos.accounting.api.v1.client.LedgerManager;
 import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
 import io.mifos.core.api.context.AutoUserContext;
+import io.mifos.core.lang.DateConverter;
 import io.mifos.core.test.fixture.TenantDataStoreContextTestRule;
 import io.mifos.core.test.listener.EnableEventRecording;
 import io.mifos.core.test.listener.EventRecorder;
@@ -53,6 +54,8 @@
 import javax.validation.Validator;
 import javax.validation.ValidatorFactory;
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -63,7 +66,7 @@
 @RunWith(SpringRunner.class)
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
         classes = {AbstractPortfolioTest.TestConfiguration.class},
-    properties = {"portfolio.bookInterestAsUser=interest_user", "portfolio.bookInterestInTimeSlot=0"}
+    properties = {"portfolio.bookLateFeesAndInterestAsUser=interest_user", "portfolio.bookInterestInTimeSlot=0", "portfolio.checkForLatenessInTimeSlot=0"}
 )
 public class AbstractPortfolioTest extends SuiteTestEnvironment {
   private static final String LOGGER_NAME = "test-logger";
@@ -188,7 +191,15 @@
                           final List<AccountAssignment> oneTimeAccountAssignments,
                           final String event,
                           final Case.State nextState) throws InterruptedException {
-    checkStateTransfer(productIdentifier, caseIdentifier, action, oneTimeAccountAssignments, BigDecimal.ZERO, event, nextState);
+    checkStateTransfer(
+        productIdentifier,
+        caseIdentifier,
+        action,
+        oneTimeAccountAssignments,
+        BigDecimal.ZERO,
+        event,
+        midnightToday(),
+        nextState);
   }
 
   void checkStateTransfer(final String productIdentifier,
@@ -197,13 +208,14 @@
                           final List<AccountAssignment> oneTimeAccountAssignments,
                           final BigDecimal paymentSize,
                           final String event,
+                          final LocalDateTime eventDateTime,
                           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)));
+    Assert.assertTrue(eventRecorder.wait(event, new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(eventDateTime))));
 
     final Case customerCase = portfolioManager.getCase(productIdentifier, caseIdentifier);
     Assert.assertEquals(nextState.name(), customerCase.getCurrentState());
@@ -313,4 +325,7 @@
     Assert.assertTrue(eventRecorder.wait(EventConstants.PUT_TASK_INSTANCE_EXECUTION, new TaskInstanceEvent(product.getIdentifier(), customerCase.getIdentifier(), taskDefinition.getIdentifier())));
   }
 
+  LocalDateTime midnightToday() {
+    return LocalDateTime.now().truncatedTo(ChronoUnit.DAYS);
+  }
 }
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 dc3cdfe..69e1c0b 100644
--- a/component-test/src/main/java/io/mifos/portfolio/Fixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
@@ -141,6 +141,7 @@
     ret.setMaximumBalance(fixScale(BigDecimal.valueOf(2000L)));
     ret.setTermRange(new TermRange(ChronoUnit.MONTHS, 18));
     ret.setPaymentCycle(new PaymentCycle(ChronoUnit.MONTHS, 1, 1, null, null));
+    ret.setPaymentSize(BigDecimal.ONE.negate().setScale(4, RoundingMode.HALF_EVEN));
 
     final CreditWorthinessSnapshot customerCreditWorthinessSnapshot = new CreditWorthinessSnapshot();
     customerCreditWorthinessSnapshot.setForCustomer("alice");
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 c4a9e5f..b34a0ed 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
@@ -41,8 +41,6 @@
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -319,6 +317,7 @@
         Collections.singletonList(assignEntryToTeller()),
         amount,
         IndividualLoanEventConstants.DISBURSE_INDIVIDUALLOAN_CASE,
+        midnightToday(),
         Case.State.ACTIVE);
     checkNextActionsCorrect(product.getIdentifier(), customerCase.getIdentifier(), Action.APPLY_INTEREST,
         Action.APPLY_INTEREST, Action.MARK_LATE, Action.ACCEPT_PAYMENT, Action.DISBURSE, Action.WRITE_OFF, Action.CLOSE);
@@ -342,7 +341,7 @@
   private void step6CalculateInterestAccrual() throws InterruptedException {
     logger.info("step6CalculateInterestAccrual");
     final String beatIdentifier = "alignment0";
-    final String midnightTimeStamp = DateConverter.toIsoString(LocalDateTime.now().truncatedTo(ChronoUnit.DAYS));
+    final String midnightTimeStamp = DateConverter.toIsoString(midnightToday());
 
     AccountingFixture.mockBalance(customerLoanAccountIdentifier, expectedCurrentBalance.negate());
 
@@ -362,7 +361,7 @@
         new BeatPublishEvent(EventConstants.DESTINATION, beatIdentifier, midnightTimeStamp)));
 
     Assert.assertTrue(eventRecorder.wait(IndividualLoanEventConstants.APPLY_INTEREST_INDIVIDUALLOAN_CASE,
-        new IndividualLoanCommandEvent(product.getIdentifier(), customerCase.getIdentifier())));
+        new IndividualLoanCommandEvent(product.getIdentifier(), customerCase.getIdentifier(), midnightTimeStamp)));
 
 
     final Case customerCaseAfterStateChange = portfolioManager.getCase(product.getIdentifier(), customerCase.getIdentifier());
@@ -408,6 +407,7 @@
         Collections.singletonList(assignEntryToTeller()),
         amount,
         IndividualLoanEventConstants.ACCEPT_PAYMENT_INDIVIDUALLOAN_CASE,
+        midnightToday(),
         Case.State.ACTIVE); //Close has to be done explicitly.
     checkNextActionsCorrect(product.getIdentifier(), customerCase.getIdentifier(), Action.APPLY_INTEREST,
         Action.APPLY_INTEREST, Action.MARK_LATE, Action.ACCEPT_PAYMENT, Action.DISBURSE, Action.WRITE_OFF, Action.CLOSE);
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 1ea1bdf..cb83144 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -351,7 +351,7 @@
       final BigDecimal forPaymentSize) {
     final Action action = Action.valueOf(actionIdentifier);
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(productIdentifier, caseIdentifier, Collections.emptyList());
-    final Case.State caseState = Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState());
+    final Case.State caseState = Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState());
     checkActionCanBeExecuted(caseState, action);
 
     return costComponentService.getCostComponentsForAction(action, dataContextOfAction, forPaymentSize)
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/ApplyInterestCommand.java b/service/src/main/java/io/mifos/individuallending/internal/command/ApplyInterestCommand.java
index 1e658f3..5c7e3ad 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/command/ApplyInterestCommand.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/command/ApplyInterestCommand.java
@@ -21,10 +21,12 @@
 public class ApplyInterestCommand {
   private final String productIdentifier;
   private final String caseIdentifier;
+  private final String forTime;
 
-  public ApplyInterestCommand(String productIdentifier, String caseIdentifier) {
+  public ApplyInterestCommand(String productIdentifier, String caseIdentifier, String forTime) {
     this.productIdentifier = productIdentifier;
     this.caseIdentifier = caseIdentifier;
+    this.forTime = forTime;
   }
 
   public String getProductIdentifier() {
@@ -35,11 +37,16 @@
     return caseIdentifier;
   }
 
+  public String getForTime() {
+    return forTime;
+  }
+
   @Override
   public String toString() {
     return "ApplyInterestCommand{" +
         "productIdentifier='" + productIdentifier + '\'' +
         ", caseIdentifier='" + caseIdentifier + '\'' +
+        ", forTime='" + forTime + '\'' +
         '}';
   }
 }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/CheckLateCommand.java b/service/src/main/java/io/mifos/individuallending/internal/command/CheckLateCommand.java
new file mode 100644
index 0000000..bb31db2
--- /dev/null
+++ b/service/src/main/java/io/mifos/individuallending/internal/command/CheckLateCommand.java
@@ -0,0 +1,49 @@
+/*
+ * 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.command;
+
+public class CheckLateCommand {
+  private final String productIdentifier;
+  private final String caseIdentifier;
+  private final String forTime;
+
+  public CheckLateCommand(String productIdentifier, String caseIdentifier, String forTime) {
+    this.productIdentifier = productIdentifier;
+    this.caseIdentifier = caseIdentifier;
+    this.forTime = forTime;
+  }
+
+  public String getProductIdentifier() {
+    return productIdentifier;
+  }
+
+  public String getCaseIdentifier() {
+    return caseIdentifier;
+  }
+
+  public String getForTime() {
+    return forTime;
+  }
+
+  @Override
+  public String toString() {
+    return "CheckLateCommand{" +
+        "productIdentifier='" + productIdentifier + '\'' +
+        ", caseIdentifier='" + caseIdentifier + '\'' +
+        ", forTime='" + forTime + '\'' +
+        '}';
+  }
+}
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/handler/BeatPublishCommandHandler.java b/service/src/main/java/io/mifos/individuallending/internal/command/handler/BeatPublishCommandHandler.java
index af4abc5..8f1c6e9 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/command/handler/BeatPublishCommandHandler.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/command/handler/BeatPublishCommandHandler.java
@@ -22,7 +22,12 @@
 import io.mifos.core.command.internal.CommandBus;
 import io.mifos.core.lang.ApplicationName;
 import io.mifos.core.lang.DateConverter;
+import io.mifos.individuallending.api.v1.events.IndividualLoanCommandEvent;
+import io.mifos.individuallending.api.v1.events.IndividualLoanEventConstants;
 import io.mifos.individuallending.internal.command.ApplyInterestCommand;
+import io.mifos.individuallending.internal.command.CheckLateCommand;
+import io.mifos.individuallending.internal.service.DataContextOfAction;
+import io.mifos.individuallending.internal.service.DataContextService;
 import io.mifos.portfolio.api.v1.domain.Case;
 import io.mifos.portfolio.service.config.PortfolioProperties;
 import io.mifos.portfolio.service.internal.command.CreateBeatPublishCommand;
@@ -46,6 +51,7 @@
 public class BeatPublishCommandHandler {
   private final CaseRepository caseRepository;
   private final PortfolioProperties portfolioProperties;
+  private final DataContextService dataContextService;
   private final ApplicationName applicationName;
   private final CommandBus commandBus;
 
@@ -53,10 +59,12 @@
   public BeatPublishCommandHandler(
       final CaseRepository caseRepository,
       final PortfolioProperties portfolioProperties,
+      final DataContextService dataContextService,
       final ApplicationName applicationName,
       final CommandBus commandBus) {
     this.caseRepository = caseRepository;
     this.portfolioProperties = portfolioProperties;
+    this.dataContextService = dataContextService;
     this.applicationName = applicationName;
     this.commandBus = commandBus;
   }
@@ -71,11 +79,42 @@
     {
       final Stream<CaseEntity> activeCases = caseRepository.findByCurrentStateIn(Collections.singleton(Case.State.ACTIVE.name()));
       activeCases.forEach(activeCase -> {
-        final ApplyInterestCommand applyInterestCommand = new ApplyInterestCommand(activeCase.getProductIdentifier(), activeCase.getIdentifier());
+        final ApplyInterestCommand applyInterestCommand = new ApplyInterestCommand(
+            activeCase.getProductIdentifier(),
+            activeCase.getIdentifier(),
+            instance.getForTime());
         commandBus.dispatch(applyInterestCommand);
       });
     }
 
+    if (portfolioProperties.getCheckForLatenessInTimeSlot() == forTime.getHour())
+    {
+      final Stream<CaseEntity> activeCases = caseRepository.findByCurrentStateIn(Collections.singleton(Case.State.ACTIVE.name()));
+      activeCases.forEach(activeCase -> {
+        final CheckLateCommand checkLateCommand = new CheckLateCommand(
+            activeCase.getProductIdentifier(),
+            activeCase.getIdentifier(),
+            instance.getForTime());
+        commandBus.dispatch(checkLateCommand);
+      });
+    }
+
     return new BeatPublishEvent(applicationName.toString(), instance.getIdentifier(), instance.getForTime());
   }
+
+  @Transactional
+  @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+  @EventEmitter(
+      selectorName = io.mifos.portfolio.api.v1.events.EventConstants.SELECTOR_NAME,
+      selectorValue = IndividualLoanEventConstants.SELECTOR_CHECK_LATE_INDIVIDUALLOAN_CASE)
+  public IndividualLoanCommandEvent process(final CheckLateCommand command) {
+    final String productIdentifier = command.getProductIdentifier();
+    final String caseIdentifier = command.getCaseIdentifier();
+    final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
+        productIdentifier, caseIdentifier, Collections.emptyList());
+
+//TODO:
+
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, command.getForTime());
+  }
 }
\ No newline at end of file
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 00740d5..5d105b4 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
@@ -20,13 +20,16 @@
 import io.mifos.core.command.annotation.CommandHandler;
 import io.mifos.core.command.annotation.CommandLogLevel;
 import io.mifos.core.command.annotation.EventEmitter;
+import io.mifos.core.lang.DateConverter;
 import io.mifos.core.lang.ServiceException;
 import io.mifos.individuallending.IndividualLendingPatternFactory;
+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.workflow.Action;
 import io.mifos.individuallending.api.v1.events.IndividualLoanCommandEvent;
 import io.mifos.individuallending.api.v1.events.IndividualLoanEventConstants;
 import io.mifos.individuallending.internal.command.*;
+import io.mifos.individuallending.internal.repository.CaseParametersRepository;
 import io.mifos.individuallending.internal.service.*;
 import io.mifos.portfolio.api.v1.domain.AccountAssignment;
 import io.mifos.portfolio.api.v1.domain.Case;
@@ -44,10 +47,10 @@
 
 import javax.annotation.Nullable;
 import java.math.BigDecimal;
+import java.time.Clock;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
-import java.time.ZoneId;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -65,6 +68,7 @@
   private final CostComponentService costComponentService;
   private final AccountingAdapter accountingAdapter;
   private final TaskInstanceRepository taskInstanceRepository;
+  private final CaseParametersRepository caseParametersRepository;
 
   @Autowired
   public IndividualLoanCommandHandler(
@@ -72,12 +76,14 @@
       final DataContextService dataContextService,
       final CostComponentService costComponentService,
       final AccountingAdapter accountingAdapter,
-      final TaskInstanceRepository taskInstanceRepository) {
+      final TaskInstanceRepository taskInstanceRepository,
+      final CaseParametersRepository caseParametersRepository) {
     this.caseRepository = caseRepository;
     this.dataContextService = dataContextService;
     this.costComponentService = costComponentService;
     this.accountingAdapter = accountingAdapter;
     this.taskInstanceRepository = taskInstanceRepository;
+    this.caseParametersRepository = caseParametersRepository;
   }
 
   @Transactional
@@ -90,7 +96,7 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
             productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.OPEN);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.OPEN);
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.OPEN);
 
@@ -109,16 +115,18 @@
         .map(Optional::get)
         .collect(Collectors.toList());
 
+    final LocalDateTime today = today();
+
     accountingAdapter.bookCharges(charges,
         command.getCommand().getNote(),
         dataContextOfAction.getMessageForCharge(Action.OPEN),
         Action.OPEN.getTransactionType());
     //Only move to new state if book charges command was accepted.
-    final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
+    final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
     customerCase.setCurrentState(Case.State.PENDING.name());
     caseRepository.save(customerCase);
 
-    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier);
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
   }
 
   @Transactional
@@ -131,7 +139,7 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.DENY);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.DENY);
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.DENY);
 
@@ -150,11 +158,13 @@
         .map(Optional::get)
         .collect(Collectors.toList());
 
-    final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
+    final LocalDateTime today = today();
+
+    final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
     customerCase.setCurrentState(Case.State.CLOSED.name());
     caseRepository.save(customerCase);
 
-    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier);
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
   }
 
   @Transactional
@@ -167,7 +177,7 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.APPROVE);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.APPROVE);
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.APPROVE);
 
@@ -178,12 +188,12 @@
     designatorToAccountIdentifierMapper.getLedgersNeedingAccounts()
             .map(ledger ->
                     new AccountAssignment(ledger.getDesignator(),
-                            accountingAdapter.createAccountForLedgerAssignment(dataContextOfAction.getCaseParameters().getCustomerIdentifier(), ledger)))
-            .map(accountAssignment -> CaseMapper.map(accountAssignment, dataContextOfAction.getCustomerCase()))
+                            accountingAdapter.createAccountForLedgerAssignment(dataContextOfAction.getCaseParametersEntity().getCustomerIdentifier(), ledger)))
+            .map(accountAssignment -> CaseMapper.map(accountAssignment, dataContextOfAction.getCustomerCaseEntity()))
             .forEach(caseAccountAssignmentEntity ->
-              dataContextOfAction.getCustomerCase().getAccountAssignments().add(caseAccountAssignmentEntity)
+              dataContextOfAction.getCustomerCaseEntity().getAccountAssignments().add(caseAccountAssignmentEntity)
             );
-    caseRepository.save(dataContextOfAction.getCustomerCase());
+    caseRepository.save(dataContextOfAction.getCustomerCaseEntity());
 
     final CostComponentsForRepaymentPeriod costComponentsForRepaymentPeriod =
         costComponentService.getCostComponentsForApprove(dataContextOfAction);
@@ -197,17 +207,19 @@
         .map(Optional::get)
         .collect(Collectors.toList());
 
+    final LocalDateTime today = today();
+
     accountingAdapter.bookCharges(charges,
         command.getCommand().getNote(),
         dataContextOfAction.getMessageForCharge(Action.APPROVE),
         Action.APPROVE.getTransactionType());
 
     //Only move to new state if book charges command was accepted.
-    final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
+    final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
     customerCase.setCurrentState(Case.State.APPROVED.name());
     caseRepository.save(customerCase);
 
-    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier);
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
   }
 
   @Transactional
@@ -218,7 +230,7 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.DISBURSE);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.DISBURSE);
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.DISBURSE);
 
@@ -239,22 +251,33 @@
             .map(Optional::get)
         .collect(Collectors.toList());
 
+    final LocalDateTime today = today();
+
     accountingAdapter.bookCharges(charges,
         command.getCommand().getNote(),
         dataContextOfAction.getMessageForCharge(Action.DISBURSE),
         Action.DISBURSE.getTransactionType());
     //Only move to new state if book charges command was accepted.
-    if (Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()) != Case.State.ACTIVE) {
-      final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
+    if (Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()) != Case.State.ACTIVE) {
+      final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
       final LocalDateTime endOfTerm
-          = ScheduledActionHelpers.getRoughEndDate(LocalDate.now(ZoneId.of("UTC")), dataContextOfAction.getCaseParameters())
+          = ScheduledActionHelpers.getRoughEndDate(today.toLocalDate(), dataContextOfAction.getCaseParameters())
           .atTime(LocalTime.MIDNIGHT);
       customerCase.setEndOfTerm(endOfTerm);
       customerCase.setCurrentState(Case.State.ACTIVE.name());
       caseRepository.save(customerCase);
     }
+    final String customerLoanAccountIdentifier = designatorToAccountIdentifierMapper.mapOrThrow(AccountDesignators.CUSTOMER_LOAN);
+    final BigDecimal currentBalance = accountingAdapter.getCurrentBalance(customerLoanAccountIdentifier).negate();
 
-    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier);
+    final BigDecimal newLoanPaymentSize = costComponentService.getLoanPaymentSize(
+        currentBalance.add(disbursalAmount),
+        dataContextOfAction);
+
+    dataContextOfAction.getCaseParametersEntity().setPaymentSize(newLoanPaymentSize);
+    caseParametersRepository.save(dataContextOfAction.getCaseParametersEntity());
+
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
   }
 
   @Transactional
@@ -267,9 +290,9 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, null);
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.APPLY_INTEREST);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.APPLY_INTEREST);
 
-    if (dataContextOfAction.getCustomerCase().getEndOfTerm() == null)
+    if (dataContextOfAction.getCustomerCaseEntity().getEndOfTerm() == null)
       throw ServiceException.internalError(
           "End of term not set for active case ''{0}.{1}.''", productIdentifier, caseIdentifier);
 
@@ -293,7 +316,7 @@
         dataContextOfAction.getMessageForCharge(Action.APPLY_INTEREST),
         Action.APPLY_INTEREST.getTransactionType());
 
-    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier);
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, command.getForTime());
   }
 
   @Transactional
@@ -306,11 +329,11 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.ACCEPT_PAYMENT);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.ACCEPT_PAYMENT);
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.ACCEPT_PAYMENT);
 
-    if (dataContextOfAction.getCustomerCase().getEndOfTerm() == null)
+    if (dataContextOfAction.getCustomerCaseEntity().getEndOfTerm() == null)
       throw ServiceException.internalError(
           "End of term not set for active case ''{0}.{1}.''", productIdentifier, caseIdentifier);
 
@@ -334,13 +357,14 @@
         .map(Optional::get)
         .collect(Collectors.toList());
 
+    final LocalDateTime today = today();
 
     accountingAdapter.bookCharges(charges,
         command.getCommand().getNote(),
         dataContextOfAction.getMessageForCharge(Action.ACCEPT_PAYMENT),
         Action.ACCEPT_PAYMENT.getTransactionType());
 
-    return new IndividualLoanCommandEvent(command.getProductIdentifier(), command.getCaseIdentifier());
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
   }
 
   @Transactional
@@ -351,14 +375,17 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.WRITE_OFF);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.WRITE_OFF);
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.WRITE_OFF);
 
-    final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
+    final LocalDateTime today = today();
+
+    final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
     customerCase.setCurrentState(Case.State.CLOSED.name());
     caseRepository.save(customerCase);
-    return new IndividualLoanCommandEvent(command.getProductIdentifier(), command.getCaseIdentifier());
+
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
   }
 
   @Transactional
@@ -369,7 +396,7 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.CLOSE);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.CLOSE);
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.CLOSE);
 
@@ -389,15 +416,18 @@
             .map(Optional::get)
             .collect(Collectors.toList());
 
+    final LocalDateTime today = today();
+
     accountingAdapter.bookCharges(charges,
         command.getCommand().getNote(),
         dataContextOfAction.getMessageForCharge(Action.DISBURSE),
         Action.DISBURSE.getTransactionType());
 
-    final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
+    final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
     customerCase.setCurrentState(Case.State.CLOSED.name());
     caseRepository.save(customerCase);
-    return new IndividualLoanCommandEvent(command.getProductIdentifier(), command.getCaseIdentifier());
+
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
   }
 
   @Transactional
@@ -408,14 +438,17 @@
     final String caseIdentifier = command.getCaseIdentifier();
     final DataContextOfAction dataContextOfAction = dataContextService.checkedGetDataContext(
         productIdentifier, caseIdentifier, command.getCommand().getOneTimeAccountAssignments());
-    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCase().getCurrentState()), Action.RECOVER);
+    IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.RECOVER);
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.RECOVER);
 
-    final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
+    final LocalDateTime today = today();
+
+    final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
     customerCase.setCurrentState(Case.State.CLOSED.name());
     caseRepository.save(customerCase);
-    return new IndividualLoanCommandEvent(command.getProductIdentifier(), command.getCaseIdentifier());
+
+    return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
   }
 
   private static Optional<ChargeInstance> mapCostComponentEntryToChargeInstance(
@@ -461,12 +494,16 @@
   }
 
   private void checkIfTasksAreOutstanding(final DataContextOfAction dataContextOfAction, final Action action) {
-    final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
-    final String caseIdentifier = dataContextOfAction.getCustomerCase().getIdentifier();
+    final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+    final String caseIdentifier = dataContextOfAction.getCustomerCaseEntity().getIdentifier();
     final boolean tasksOutstanding = taskInstanceRepository.areTasksOutstanding(
         productIdentifier, caseIdentifier, action.name());
     if (tasksOutstanding)
       throw ServiceException.conflict("Cannot execute action ''{0}'' for case ''{1}.{2}'' because tasks are incomplete.",
           action.name(), productIdentifier, caseIdentifier);
   }
+
+  private LocalDateTime today() {
+    return LocalDate.now(Clock.systemUTC()).atStartOfDay();
+  }
 }
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/individuallending/internal/mapper/CaseParametersMapper.java b/service/src/main/java/io/mifos/individuallending/internal/mapper/CaseParametersMapper.java
index 1655266..c1231f1 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/mapper/CaseParametersMapper.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/mapper/CaseParametersMapper.java
@@ -34,7 +34,9 @@
  */
 public class CaseParametersMapper {
 
-  public static CaseParametersEntity map(final Long caseId, final CaseParameters instance) {
+  public static CaseParametersEntity map(
+      final Long caseId,
+      final CaseParameters instance) {
     final CaseParametersEntity ret = new CaseParametersEntity();
 
     ret.setCaseId(caseId);
@@ -49,6 +51,7 @@
     ret.setPaymentCycleAlignmentWeek(instance.getPaymentCycle().getAlignmentWeek());
     ret.setPaymentCycleAlignmentMonth(instance.getPaymentCycle().getAlignmentMonth());
     ret.setCreditWorthinessFactors(mapSnapshotsToFactors(instance.getCreditWorthinessSnapshots(), ret));
+    ret.setPaymentSize(BigDecimal.ONE.negate()); //semaphore for not yet set.
 
     return ret;
   }
@@ -56,6 +59,8 @@
   public static Set<CaseCreditWorthinessFactorEntity> mapSnapshotsToFactors(
           final List<CreditWorthinessSnapshot> creditWorthinessSnapshots,
           final CaseParametersEntity caseParametersEntity) {
+    if (creditWorthinessSnapshots == null)
+      return Collections.emptySet();
     return Stream.iterate(0, i -> i+1).limit(creditWorthinessSnapshots.size())
             .flatMap(i -> mapSnapshotToFactors(
                     creditWorthinessSnapshots.get(i), i, caseParametersEntity)).collect(Collectors.toSet());
@@ -144,6 +149,7 @@
     ret.setTermRange(getTermRange(caseParametersEntity));
     ret.setMaximumBalance(caseParametersEntity.getBalanceRangeMaximum().setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN));
     ret.setPaymentCycle(getPaymentCycle(caseParametersEntity));
+    ret.setPaymentSize(caseParametersEntity.getPaymentSize());
     return ret;
   }
 
diff --git a/service/src/main/java/io/mifos/individuallending/internal/repository/CaseParametersEntity.java b/service/src/main/java/io/mifos/individuallending/internal/repository/CaseParametersEntity.java
index b9ecf6b..00d6176 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/repository/CaseParametersEntity.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/repository/CaseParametersEntity.java
@@ -70,6 +70,9 @@
   @OneToMany(targetEntity = CaseCreditWorthinessFactorEntity.class, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "caseId")
   private Set<CaseCreditWorthinessFactorEntity> creditWorthinessFactors;
 
+  @Column(name = "payment_size")
+  private BigDecimal paymentSize;
+
   public CaseParametersEntity() {
   }
 
@@ -177,6 +180,14 @@
     this.creditWorthinessFactors = creditWorthinessFactors;
   }
 
+  public BigDecimal getPaymentSize() {
+    return paymentSize;
+  }
+
+  public void setPaymentSize(BigDecimal paymentSize) {
+    this.paymentSize = paymentSize;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
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 c3ba820..5f73add 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
@@ -16,10 +16,10 @@
 package io.mifos.individuallending.internal.service;
 
 import io.mifos.core.lang.ServiceException;
-import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
 import io.mifos.individuallending.api.v1.domain.product.AccountDesignators;
 import io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator;
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
+import io.mifos.individuallending.internal.repository.CaseParametersEntity;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
 import io.mifos.portfolio.service.internal.util.AccountingAdapter;
@@ -89,9 +89,9 @@
   }
 
   public CostComponentsForRepaymentPeriod getCostComponentsForOpen(final DataContextOfAction dataContextOfAction) {
-    final CaseParameters caseParameters = dataContextOfAction.getCaseParameters();
-    final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
-    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
+    final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+    final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
     final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.OPEN, today()));
     final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier, scheduledActions);
@@ -99,7 +99,7 @@
     return getCostComponentsForScheduledCharges(
         Collections.emptyMap(),
         scheduledCharges,
-        caseParameters.getMaximumBalance(),
+        caseParameters.getBalanceRangeMaximum(),
         BigDecimal.ZERO,
         BigDecimal.ZERO,
         dataContextOfAction.getInterest(),
@@ -108,9 +108,9 @@
   }
 
   public CostComponentsForRepaymentPeriod getCostComponentsForDeny(final DataContextOfAction dataContextOfAction) {
-    final CaseParameters caseParameters = dataContextOfAction.getCaseParameters();
-    final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
-    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
+    final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+    final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
     final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.DENY, today()));
     final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier, scheduledActions);
@@ -118,7 +118,7 @@
     return getCostComponentsForScheduledCharges(
         Collections.emptyMap(),
         scheduledCharges,
-        caseParameters.getMaximumBalance(),
+        caseParameters.getBalanceRangeMaximum(),
         BigDecimal.ZERO,
         BigDecimal.ZERO,
         dataContextOfAction.getInterest(),
@@ -128,9 +128,9 @@
 
   public CostComponentsForRepaymentPeriod getCostComponentsForApprove(final DataContextOfAction dataContextOfAction) {
     //Charge the approval fee if applicable.
-    final CaseParameters caseParameters = dataContextOfAction.getCaseParameters();
-    final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
-    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
+    final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+    final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
     final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.APPROVE, today()));
     final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
         productIdentifier, scheduledActions);
@@ -138,7 +138,7 @@
     return getCostComponentsForScheduledCharges(
         Collections.emptyMap(),
         scheduledCharges,
-        caseParameters.getMaximumBalance(),
+        caseParameters.getBalanceRangeMaximum(),
         BigDecimal.ZERO,
         BigDecimal.ZERO,
         dataContextOfAction.getInterest(),
@@ -155,21 +155,21 @@
     final BigDecimal currentBalance = accountingAdapter.getCurrentBalance(customerLoanAccountIdentifier).negate();
 
     if (requestedDisbursalSize != null &&
-        dataContextOfAction.getCaseParameters().getMaximumBalance().compareTo(
+        dataContextOfAction.getCaseParametersEntity().getBalanceRangeMaximum().compareTo(
         currentBalance.add(requestedDisbursalSize)) < 0)
       throw ServiceException.conflict("Cannot disburse over the maximum balance.");
 
     final Optional<LocalDateTime> optionalStartOfTerm = accountingAdapter.getDateOfOldestEntryContainingMessage(
         customerLoanAccountIdentifier,
         dataContextOfAction.getMessageForCharge(Action.DISBURSE));
-    final CaseParameters caseParameters = dataContextOfAction.getCaseParameters();
-    final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
-    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
+    final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+    final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
     final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.DISBURSE, today()));
 
     final BigDecimal disbursalSize;
     if (requestedDisbursalSize == null)
-      disbursalSize = dataContextOfAction.getCaseParameters().getMaximumBalance().negate();
+      disbursalSize = dataContextOfAction.getCaseParametersEntity().getBalanceRangeMaximum().negate();
     else
       disbursalSize = requestedDisbursalSize.negate();
 
@@ -195,7 +195,7 @@
     return getCostComponentsForScheduledCharges(
         accruedCostComponents,
         chargesSplitIntoScheduledAndAccrued.get(false),
-        caseParameters.getMaximumBalance(),
+        caseParameters.getBalanceRangeMaximum(),
         currentBalance,
         disbursalSize,
         dataContextOfAction.getInterest(),
@@ -213,9 +213,9 @@
 
     final LocalDate startOfTerm = getStartOfTermOrThrow(dataContextOfAction, customerLoanAccountIdentifier);
 
-    final CaseParameters caseParameters = dataContextOfAction.getCaseParameters();
-    final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
-    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
+    final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+    final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
     final LocalDate today = today();
     final ScheduledAction interestAction = new ScheduledAction(Action.APPLY_INTEREST, today, new Period(1, today));
 
@@ -235,7 +235,7 @@
     return getCostComponentsForScheduledCharges(
         accruedCostComponents,
         chargesSplitIntoScheduledAndAccrued.get(false),
-        caseParameters.getMaximumBalance(),
+        caseParameters.getBalanceRangeMaximum(),
         currentBalance,
         BigDecimal.ZERO,
         dataContextOfAction.getInterest(),
@@ -254,32 +254,20 @@
 
     final LocalDate startOfTerm = getStartOfTermOrThrow(dataContextOfAction, customerLoanAccountIdentifier);
 
-    final CaseParameters caseParameters = dataContextOfAction.getCaseParameters();
-    final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
-    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
+    final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+    final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
     final ScheduledAction scheduledAction
         = ScheduledActionHelpers.getNextScheduledPayment(
         startOfTerm,
-        dataContextOfAction.getCustomerCase().getEndOfTerm().toLocalDate(),
-        caseParameters
+        dataContextOfAction.getCustomerCaseEntity().getEndOfTerm().toLocalDate(),
+        dataContextOfAction.getCaseParameters()
     );
 
-    final BigDecimal loanPaymentSize;
-    if (requestedLoanPaymentSize != null)
-      loanPaymentSize = requestedLoanPaymentSize;
-    else {
-      final List<ScheduledAction> hypotheticalScheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(
-          today(),
-          caseParameters);
-      final List<ScheduledCharge> hypotheticalScheduledCharges = scheduledChargesService.getScheduledCharges(
-          productIdentifier,
-          hypotheticalScheduledActions);
-      loanPaymentSize = getLoanPaymentSize(
-          currentBalance,
-          dataContextOfAction.getInterest(),
-          minorCurrencyUnitDigits,
-          hypotheticalScheduledCharges);
-    }
+    final BigDecimal loanPaymentSize =
+        (requestedLoanPaymentSize != null) ?
+            requestedLoanPaymentSize :
+            dataContextOfAction.getCaseParametersEntity().getPaymentSize();
 
     final List<ScheduledCharge> scheduledChargesForThisAction = scheduledChargesService.getScheduledCharges(
         productIdentifier,
@@ -299,7 +287,7 @@
     return getCostComponentsForScheduledCharges(
         accruedCostComponents,
         chargesSplitIntoScheduledAndAccrued.get(false),
-        caseParameters.getMaximumBalance(),
+        caseParameters.getBalanceRangeMaximum(),
         currentBalance,
         loanPaymentSize,
         dataContextOfAction.getInterest(),
@@ -361,9 +349,9 @@
 
     final LocalDate startOfTerm = getStartOfTermOrThrow(dataContextOfAction, customerLoanAccountIdentifier);
 
-    final CaseParameters caseParameters = dataContextOfAction.getCaseParameters();
-    final String productIdentifier = dataContextOfAction.getProduct().getIdentifier();
-    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
+    final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+    final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
     final LocalDate today = today();
     final ScheduledAction closeAction = new ScheduledAction(Action.CLOSE, today, new Period(1, today));
 
@@ -383,7 +371,7 @@
     return getCostComponentsForScheduledCharges(
         accruedCostComponents,
         chargesSplitIntoScheduledAndAccrued.get(false),
-        caseParameters.getMaximumBalance(),
+        caseParameters.getBalanceRangeMaximum(),
         currentBalance,
         BigDecimal.ZERO,
         dataContextOfAction.getInterest(),
@@ -534,6 +522,22 @@
     }
   }
 
+  public BigDecimal getLoanPaymentSize(
+      final BigDecimal assumedBalance,
+      final DataContextOfAction dataContextOfAction) {
+    final List<ScheduledAction> hypotheticalScheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(
+        today(),
+        dataContextOfAction.getCaseParameters());
+    final List<ScheduledCharge> hypotheticalScheduledCharges = scheduledChargesService.getScheduledCharges(
+        dataContextOfAction.getProductEntity().getIdentifier(),
+        hypotheticalScheduledActions);
+    return getLoanPaymentSize(
+        assumedBalance,
+        dataContextOfAction.getInterest(),
+        dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits(),
+        hypotheticalScheduledCharges);
+  }
+
   static BigDecimal getLoanPaymentSize(final BigDecimal startingBalance,
                                        final BigDecimal interest,
                                        final int minorCurrencyUnitDigits,
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/DataContextOfAction.java b/service/src/main/java/io/mifos/individuallending/internal/service/DataContextOfAction.java
index 4cb7b5d..3d0e968 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/DataContextOfAction.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/DataContextOfAction.java
@@ -17,6 +17,8 @@
 
 import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
+import io.mifos.individuallending.internal.mapper.CaseParametersMapper;
+import io.mifos.individuallending.internal.repository.CaseParametersEntity;
 import io.mifos.portfolio.api.v1.domain.AccountAssignment;
 import io.mifos.portfolio.service.internal.repository.CaseEntity;
 import io.mifos.portfolio.service.internal.repository.ProductEntity;
@@ -33,12 +35,12 @@
 public class DataContextOfAction {
   private final ProductEntity product;
   private final CaseEntity customerCase;
-  private final CaseParameters caseParameters;
+  private final CaseParametersEntity caseParameters;
   private final List<AccountAssignment> oneTimeAccountAssignments;
 
   DataContextOfAction(final @Nonnull ProductEntity product,
                       final @Nonnull CaseEntity customerCase,
-                      final @Nonnull CaseParameters caseParameters,
+                      final @Nonnull CaseParametersEntity caseParameters,
                       final @Nullable List<AccountAssignment> oneTimeAccountAssignments) {
     this.product = product;
     this.customerCase = customerCase;
@@ -46,18 +48,22 @@
     this.oneTimeAccountAssignments = oneTimeAccountAssignments == null ? Collections.emptyList() : oneTimeAccountAssignments;
   }
 
-  public @Nonnull ProductEntity getProduct() {
+  public @Nonnull ProductEntity getProductEntity() {
     return product;
   }
 
-  public @Nonnull CaseEntity getCustomerCase() {
+  public @Nonnull CaseEntity getCustomerCaseEntity() {
     return customerCase;
   }
 
-  public @Nonnull CaseParameters getCaseParameters() {
+  public @Nonnull CaseParametersEntity getCaseParametersEntity() {
     return caseParameters;
   }
 
+  public @Nonnull CaseParameters getCaseParameters() {
+    return CaseParametersMapper.mapEntity(caseParameters, product.getMinorCurrencyUnitDigits());
+  }
+
   @Nonnull List<AccountAssignment> getOneTimeAccountAssignments() {
     return oneTimeAccountAssignments;
   }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/DataContextService.java b/service/src/main/java/io/mifos/individuallending/internal/service/DataContextService.java
index 225d0a6..388ae0c 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/DataContextService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/DataContextService.java
@@ -16,8 +16,7 @@
 package io.mifos.individuallending.internal.service;
 
 import io.mifos.core.lang.ServiceException;
-import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
-import io.mifos.individuallending.internal.mapper.CaseParametersMapper;
+import io.mifos.individuallending.internal.repository.CaseParametersEntity;
 import io.mifos.individuallending.internal.repository.CaseParametersRepository;
 import io.mifos.portfolio.api.v1.domain.AccountAssignment;
 import io.mifos.portfolio.service.internal.repository.CaseEntity;
@@ -61,9 +60,8 @@
         caseRepository.findByProductIdentifierAndIdentifier(productIdentifier, caseIdentifier)
             .orElseThrow(() -> ServiceException.notFound("Case not found ''{0}.{1}''.", productIdentifier, caseIdentifier));
 
-    final CaseParameters caseParameters =
+    final CaseParametersEntity caseParameters =
         caseParametersRepository.findByCaseId(customerCase.getId())
-            .map(x -> CaseParametersMapper.mapEntity(x, product.getMinorCurrencyUnitDigits()))
             .orElseThrow(() -> ServiceException.notFound(
                 "Individual loan not found ''{0}.{1}''.",
                 productIdentifier, caseIdentifier));
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/DesignatorToAccountIdentifierMapper.java b/service/src/main/java/io/mifos/individuallending/internal/service/DesignatorToAccountIdentifierMapper.java
index 5b2fb94..2bd9e57 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/DesignatorToAccountIdentifierMapper.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/DesignatorToAccountIdentifierMapper.java
@@ -37,8 +37,8 @@
   private final @Nonnull List<AccountAssignment> oneTimeAccountAssignments;
 
   public DesignatorToAccountIdentifierMapper(final @Nonnull DataContextOfAction dataContextOfAction) {
-    this.productAccountAssignments = dataContextOfAction.getProduct().getAccountAssignments();
-    this.caseAccountAssignments = dataContextOfAction.getCustomerCase().getAccountAssignments();
+    this.productAccountAssignments = dataContextOfAction.getProductEntity().getAccountAssignments();
+    this.caseAccountAssignments = dataContextOfAction.getCustomerCaseEntity().getAccountAssignments();
     this.oneTimeAccountAssignments = dataContextOfAction.getOneTimeAccountAssignments();
   }
 
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 e836004..6108651 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
@@ -45,20 +45,20 @@
       final int pageIndex,
       final int size,
       final @Nonnull LocalDate initialDisbursalDate) {
-    final int minorCurrencyUnitDigits = dataContextOfAction.getProduct().getMinorCurrencyUnitDigits();
+    final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
 
     final List<ScheduledAction> scheduledActions = ScheduledActionHelpers.getHypotheticalScheduledActions(initialDisbursalDate, dataContextOfAction.getCaseParameters());
 
-    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(dataContextOfAction.getProduct().getIdentifier(), scheduledActions);
+    final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(dataContextOfAction.getProductEntity().getIdentifier(), scheduledActions);
 
     final BigDecimal loanPaymentSize = CostComponentService.getLoanPaymentSize(
-        dataContextOfAction.getCaseParameters().getMaximumBalance(),
+        dataContextOfAction.getCaseParametersEntity().getBalanceRangeMaximum(),
         dataContextOfAction.getInterest(),
         minorCurrencyUnitDigits,
         scheduledCharges);
 
     final List<PlannedPayment> plannedPaymentsElements = getPlannedPaymentsElements(
-        dataContextOfAction.getCaseParameters().getMaximumBalance(),
+        dataContextOfAction.getCaseParametersEntity().getBalanceRangeMaximum(),
         minorCurrencyUnitDigits,
         scheduledCharges,
         loanPaymentSize,
diff --git a/service/src/main/java/io/mifos/portfolio/service/config/PortfolioProperties.java b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioProperties.java
index 25cf294..08beb6b 100644
--- a/service/src/main/java/io/mifos/portfolio/service/config/PortfolioProperties.java
+++ b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioProperties.java
@@ -30,20 +30,23 @@
 @Validated
 public class PortfolioProperties {
   @ValidIdentifier
-  private String bookInterestAsUser;
+  private String bookLateFeesAndInterestAsUser;
 
   @Range(min=0, max=23)
   private int bookInterestInTimeSlot = 0;
 
+  @Range(min=0, max=23)
+  private int checkForLatenessInTimeSlot = 0;
+
   public PortfolioProperties() {
   }
 
-  public String getBookInterestAsUser() {
-    return bookInterestAsUser;
+  public String getBookLateFeesAndInterestAsUser() {
+    return bookLateFeesAndInterestAsUser;
   }
 
-  public void setBookInterestAsUser(String bookInterestAsUser) {
-    this.bookInterestAsUser = bookInterestAsUser;
+  public void setBookLateFeesAndInterestAsUser(String bookLateFeesAndInterestAsUser) {
+    this.bookLateFeesAndInterestAsUser = bookLateFeesAndInterestAsUser;
   }
 
   public int getBookInterestInTimeSlot() {
@@ -53,4 +56,12 @@
   public void setBookInterestInTimeSlot(int bookInterestInTimeSlot) {
     this.bookInterestInTimeSlot = bookInterestInTimeSlot;
   }
+
+  public int getCheckForLatenessInTimeSlot() {
+    return checkForLatenessInTimeSlot;
+  }
+
+  public void setCheckForLatenessInTimeSlot(int checkForLatenessInTimeSlot) {
+    this.checkForLatenessInTimeSlot = checkForLatenessInTimeSlot;
+  }
 }
diff --git a/service/src/main/resources/db/migrations/mariadb/V8__late_payment_determination.sql b/service/src/main/resources/db/migrations/mariadb/V8__late_payment_determination.sql
new file mode 100644
index 0000000..cb3bc59
--- /dev/null
+++ b/service/src/main/resources/db/migrations/mariadb/V8__late_payment_determination.sql
@@ -0,0 +1,17 @@
+--
+-- Copyright 2017 Kuelap, Inc.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--    http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+ALTER TABLE bastet_il_cases ADD COLUMN payment_size DECIMAL(19,4) NULL DEFAULT NULL;
\ No newline at end of file
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 fc31ae7..beeae57 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
@@ -23,6 +23,7 @@
 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.workflow.Action;
+import io.mifos.individuallending.internal.mapper.CaseParametersMapper;
 import io.mifos.portfolio.api.v1.domain.*;
 import io.mifos.portfolio.service.internal.repository.BalanceSegmentRepository;
 import io.mifos.portfolio.service.internal.repository.CaseEntity;
@@ -152,7 +153,7 @@
       final CaseEntity customerCase = new CaseEntity();
       customerCase.setInterest(interest);
 
-      return new DataContextOfAction(product, customerCase, caseParameters, Collections.emptyList());
+      return new DataContextOfAction(product, customerCase, CaseParametersMapper.map(1L, caseParameters), Collections.emptyList());
     }
 
     @Override