* More refactoring to facilitate unit testing.
* Added coverage for core part of interest calculation.
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 31bbc67..16ddbe7 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -28,11 +28,13 @@
 import io.mifos.individuallending.internal.repository.CaseParametersEntity;
 import io.mifos.individuallending.internal.repository.CaseParametersRepository;
 import io.mifos.individuallending.internal.repository.CreditWorthinessFactorType;
+import io.mifos.individuallending.internal.service.DesignatorToAccountIdentifierMapper;
 import io.mifos.individuallending.internal.service.costcomponent.*;
 import io.mifos.individuallending.internal.service.DataContextOfAction;
 import io.mifos.individuallending.internal.service.DataContextService;
 import io.mifos.portfolio.api.v1.domain.*;
 import io.mifos.portfolio.service.ServiceConstants;
+import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import io.mifos.products.spi.PatternFactory;
 import io.mifos.products.spi.ProductCommandDispatcher;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -126,6 +128,7 @@
   private final MarkLatePaymentBuilderService markLatePaymentBuilderService;
   private final WriteOffPaymentBuilderService writeOffPaymentBuilderService;
   private final RecoverPaymentBuilderService recoverPaymentBuilderService;
+  private final AccountingAdapter accountingAdapter;
   private final CustomerManager customerManager;
   private final IndividualLendingCommandDispatcher individualLendingCommandDispatcher;
   private final Gson gson;
@@ -144,7 +147,7 @@
       final MarkLatePaymentBuilderService markLatePaymentBuilderService,
       final WriteOffPaymentBuilderService writeOffPaymentBuilderService,
       final RecoverPaymentBuilderService recoverPaymentBuilderService,
-      final CustomerManager customerManager,
+      AccountingAdapter accountingAdapter, final CustomerManager customerManager,
       final IndividualLendingCommandDispatcher individualLendingCommandDispatcher,
       @Qualifier(ServiceConstants.GSON_NAME) final Gson gson)
   {
@@ -160,6 +163,7 @@
     this.markLatePaymentBuilderService = markLatePaymentBuilderService;
     this.writeOffPaymentBuilderService = writeOffPaymentBuilderService;
     this.recoverPaymentBuilderService = recoverPaymentBuilderService;
+    this.accountingAdapter = accountingAdapter;
 
     this.customerManager = customerManager;
     this.individualLendingCommandDispatcher = individualLendingCommandDispatcher;
@@ -466,10 +470,17 @@
         throw ServiceException.internalError("Invalid action: ''{0}''.", action.name());
     }
 
+    final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
+        = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
     final PaymentBuilder paymentBuilder = paymentBuilderService.getPaymentBuilder(
         dataContextOfAction,
         forPaymentSize,
-        forDate);
+        forDate,
+        runningBalances);
 
     return paymentBuilder.buildPayment(action, forAccountDesignators);
   }
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 f980983..f5110cb 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
@@ -128,11 +128,14 @@
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.OPEN);
 
-    final PaymentBuilder paymentBuilder
-        = openPaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today());
-
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
-            = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+        = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
+    final PaymentBuilder paymentBuilder
+        = openPaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today(), runningBalances);
 
     final List<ChargeInstance> charges = paymentBuilder.buildCharges(Action.OPEN, designatorToAccountIdentifierMapper);
 
@@ -165,11 +168,14 @@
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.DENY);
 
-    final PaymentBuilder paymentBuilder
-        = denyPaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today());
-
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
         = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
+    final PaymentBuilder paymentBuilder
+        = denyPaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today(), runningBalances);
 
     final List<ChargeInstance> charges = paymentBuilder.buildCharges(Action.DENY, designatorToAccountIdentifierMapper);
 
@@ -247,8 +253,12 @@
         );
     caseRepository.save(dataContextOfAction.getCustomerCaseEntity());
 
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
     final PaymentBuilder paymentBuilder =
-        approvePaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today());
+        approvePaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today(), runningBalances);
 
     final List<ChargeInstance> charges = paymentBuilder.buildCharges(Action.APPROVE, designatorToAccountIdentifierMapper);
 
@@ -281,11 +291,15 @@
     checkIfTasksAreOutstanding(dataContextOfAction, Action.DISBURSE);
 
     final BigDecimal disbursalAmount = Optional.ofNullable(command.getCommand().getPaymentSize()).orElse(BigDecimal.ZERO);
-    final PaymentBuilder paymentBuilder =
-        disbursePaymentBuilderService.getPaymentBuilder(dataContextOfAction, disbursalAmount, CostComponentService.today());
 
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
         = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
+    final PaymentBuilder paymentBuilder =
+        disbursePaymentBuilderService.getPaymentBuilder(dataContextOfAction, disbursalAmount, CostComponentService.today(), runningBalances);
 
     final List<ChargeInstance> charges = paymentBuilder.buildCharges(Action.DISBURSE, designatorToAccountIdentifierMapper);
 
@@ -337,11 +351,14 @@
       throw ServiceException.internalError(
           "End of term not set for active case ''{0}.{1}.''", productIdentifier, caseIdentifier);
 
-    final PaymentBuilder paymentBuilder =
-        applyInterestPaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today());
-
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
         = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
+    final PaymentBuilder paymentBuilder =
+        applyInterestPaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today(), runningBalances);
 
     final List<ChargeInstance> charges = paymentBuilder.buildCharges(Action.APPLY_INTEREST, designatorToAccountIdentifierMapper);
 
@@ -372,14 +389,18 @@
       throw ServiceException.internalError(
           "End of term not set for active case ''{0}.{1}.''", productIdentifier, caseIdentifier);
 
+
+    final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
+        = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
     final PaymentBuilder paymentBuilder =
         acceptPaymentBuilderService.getPaymentBuilder(
             dataContextOfAction,
             command.getCommand().getPaymentSize(),
-            DateConverter.fromIsoString(command.getCommand().getCreatedOn()).toLocalDate());
-
-    final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
-        = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+            DateConverter.fromIsoString(command.getCommand().getCreatedOn()).toLocalDate(), runningBalances);
 
     final List<ChargeInstance> charges = paymentBuilder.buildCharges(Action.ACCEPT_PAYMENT, designatorToAccountIdentifierMapper);
 
@@ -412,11 +433,15 @@
       throw ServiceException.internalError(
           "End of term not set for active case ''{0}.{1}.''", productIdentifier, caseIdentifier);
 
-    final PaymentBuilder paymentBuilder =
-        markLatePaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, DateConverter.fromIsoString(command.getForTime()).toLocalDate());
-
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
         = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
+    final PaymentBuilder paymentBuilder =
+        markLatePaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, DateConverter.fromIsoString(command.getForTime()).toLocalDate(),
+            runningBalances);
 
     final List<ChargeInstance> charges = paymentBuilder.buildCharges(Action.MARK_LATE, designatorToAccountIdentifierMapper);
 
@@ -464,11 +489,14 @@
 
     checkIfTasksAreOutstanding(dataContextOfAction, Action.CLOSE);
 
-    final PaymentBuilder paymentBuilder =
-        closePaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today());
-
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
         = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+    final RealRunningBalances runningBalances = new RealRunningBalances(
+        accountingAdapter,
+        designatorToAccountIdentifierMapper);
+
+    final PaymentBuilder paymentBuilder =
+        closePaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today(), runningBalances);
 
     final List<ChargeInstance> charges = paymentBuilder.buildCharges(Action.CLOSE, designatorToAccountIdentifierMapper);
 
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 ef5f34b..3846c37 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
@@ -19,14 +19,12 @@
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
 import io.mifos.individuallending.internal.repository.CaseParametersEntity;
 import io.mifos.individuallending.internal.service.DataContextOfAction;
-import io.mifos.individuallending.internal.service.DesignatorToAccountIdentifierMapper;
 import io.mifos.individuallending.internal.service.schedule.ScheduledAction;
 import io.mifos.individuallending.internal.service.schedule.ScheduledActionHelpers;
 import io.mifos.individuallending.internal.service.schedule.ScheduledCharge;
 import io.mifos.individuallending.internal.service.schedule.ScheduledChargesService;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
-import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -43,31 +41,17 @@
 @Service
 public class AcceptPaymentBuilderService implements PaymentBuilderService {
   private final ScheduledChargesService scheduledChargesService;
-  private final AccountingAdapter accountingAdapter;
 
   @Autowired
   public AcceptPaymentBuilderService(
-      final ScheduledChargesService scheduledChargesService,
-      final AccountingAdapter accountingAdapter) {
+      final ScheduledChargesService scheduledChargesService) {
     this.scheduledChargesService = scheduledChargesService;
-    this.accountingAdapter = accountingAdapter;
   }
 
+  @Override
   public PaymentBuilder getPaymentBuilder(
       final DataContextOfAction dataContextOfAction,
       final BigDecimal requestedLoanPaymentSize,
-      final LocalDate forDate)
-  {
-    final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
-        = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
-    final RealRunningBalances runningBalances = new RealRunningBalances(accountingAdapter, designatorToAccountIdentifierMapper);
-
-    return getPaymentBuilderHelper(dataContextOfAction, requestedLoanPaymentSize, forDate, runningBalances);
-  }
-
-  PaymentBuilder getPaymentBuilderHelper(
-      final DataContextOfAction dataContextOfAction,
-      final BigDecimal requestedLoanPaymentSize,
       final LocalDate forDate,
       final RunningBalances runningBalances) {
     final LocalDate startOfTerm = runningBalances.getStartOfTermOrThrow(dataContextOfAction);
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderService.java
index 7d0628b..7fef484 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderService.java
@@ -18,14 +18,12 @@
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
 import io.mifos.individuallending.internal.repository.CaseParametersEntity;
 import io.mifos.individuallending.internal.service.DataContextOfAction;
-import io.mifos.individuallending.internal.service.DesignatorToAccountIdentifierMapper;
 import io.mifos.individuallending.internal.service.schedule.Period;
 import io.mifos.individuallending.internal.service.schedule.ScheduledAction;
 import io.mifos.individuallending.internal.service.schedule.ScheduledCharge;
 import io.mifos.individuallending.internal.service.schedule.ScheduledChargesService;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
-import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -42,25 +40,19 @@
 @Service
 public class ApplyInterestPaymentBuilderService implements PaymentBuilderService {
   private final ScheduledChargesService scheduledChargesService;
-  private final AccountingAdapter accountingAdapter;
 
   @Autowired
-  public ApplyInterestPaymentBuilderService(
-      final ScheduledChargesService scheduledChargesService,
-      final AccountingAdapter accountingAdapter) {
+  public ApplyInterestPaymentBuilderService(final ScheduledChargesService scheduledChargesService) {
     this.scheduledChargesService = scheduledChargesService;
-    this.accountingAdapter = accountingAdapter;
   }
 
+  @Override
   public PaymentBuilder getPaymentBuilder(
       final DataContextOfAction dataContextOfAction,
       final BigDecimal ignored,
-      final LocalDate forDate)
+      final LocalDate forDate,
+      final RunningBalances runningBalances)
   {
-    final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
-        = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
-    final RunningBalances runningBalances = new RealRunningBalances(accountingAdapter, designatorToAccountIdentifierMapper);
-
     final LocalDate startOfTerm = runningBalances.getStartOfTermOrThrow(dataContextOfAction);
 
     final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApprovePaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApprovePaymentBuilderService.java
index 3f7566d..61649c7 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApprovePaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApprovePaymentBuilderService.java
@@ -42,10 +42,12 @@
     this.scheduledChargesService = scheduledChargesService;
   }
 
+  @Override
   public PaymentBuilder getPaymentBuilder(
       final DataContextOfAction dataContextOfAction,
       final BigDecimal ignored,
-      final LocalDate forDate)
+      final LocalDate forDate,
+      final RunningBalances runningBalances)
   {
     //Charge the approval fee if applicable.
     final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ClosePaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ClosePaymentBuilderService.java
index 4118d43..4c5bdfb 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ClosePaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ClosePaymentBuilderService.java
@@ -20,14 +20,12 @@
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
 import io.mifos.individuallending.internal.repository.CaseParametersEntity;
 import io.mifos.individuallending.internal.service.DataContextOfAction;
-import io.mifos.individuallending.internal.service.DesignatorToAccountIdentifierMapper;
 import io.mifos.individuallending.internal.service.schedule.Period;
 import io.mifos.individuallending.internal.service.schedule.ScheduledAction;
 import io.mifos.individuallending.internal.service.schedule.ScheduledCharge;
 import io.mifos.individuallending.internal.service.schedule.ScheduledChargesService;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
-import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -44,25 +42,20 @@
 @Service
 public class ClosePaymentBuilderService implements PaymentBuilderService {
   private final ScheduledChargesService scheduledChargesService;
-  private final AccountingAdapter accountingAdapter;
 
   @Autowired
   public ClosePaymentBuilderService(
-      final ScheduledChargesService scheduledChargesService,
-      final AccountingAdapter accountingAdapter) {
+      final ScheduledChargesService scheduledChargesService) {
     this.scheduledChargesService = scheduledChargesService;
-    this.accountingAdapter = accountingAdapter;
   }
 
+  @Override
   public PaymentBuilder getPaymentBuilder(
       final DataContextOfAction dataContextOfAction,
       final BigDecimal ignored,
-      final LocalDate forDate)
+      final LocalDate forDate,
+      final RunningBalances runningBalances)
   {
-    final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
-        = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
-    final RealRunningBalances runningBalances = new RealRunningBalances(accountingAdapter, designatorToAccountIdentifierMapper);
-
     if (runningBalances.getBalance(AccountDesignators.CUSTOMER_LOAN_GROUP).compareTo(BigDecimal.ZERO) != 0)
       throw ServiceException.conflict("Cannot close loan until the balance is zero.");
 
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DenyPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DenyPaymentBuilderService.java
index 1912617..99e90c0 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DenyPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DenyPaymentBuilderService.java
@@ -42,10 +42,12 @@
     this.scheduledChargesService = scheduledChargesService;
   }
 
+  @Override
   public PaymentBuilder getPaymentBuilder(
       final DataContextOfAction dataContextOfAction,
       final BigDecimal ignored,
-      final LocalDate forDate)
+      final LocalDate forDate,
+      final RunningBalances runningBalances)
   {
     final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
     final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DisbursePaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DisbursePaymentBuilderService.java
index 4320807..97a47eb 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DisbursePaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DisbursePaymentBuilderService.java
@@ -58,15 +58,16 @@
     this.accountingAdapter = accountingAdapter;
   }
 
+  @Override
   public PaymentBuilder getPaymentBuilder(
       final @Nonnull DataContextOfAction dataContextOfAction,
       final @Nullable BigDecimal requestedDisbursalSize,
-      final LocalDate forDate)
+      final LocalDate forDate,
+      final RunningBalances runningBalances)
   {
     final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
         = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
     final String customerLoanPrincipalAccountIdentifier = designatorToAccountIdentifierMapper.mapOrThrow(AccountDesignators.CUSTOMER_LOAN_PRINCIPAL);
-    final RealRunningBalances runningBalances = new RealRunningBalances(accountingAdapter, designatorToAccountIdentifierMapper);
     final BigDecimal currentBalance = runningBalances.getBalance(AccountDesignators.CUSTOMER_LOAN_PRINCIPAL);
 
     if (requestedDisbursalSize != null &&
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/MarkLatePaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/MarkLatePaymentBuilderService.java
index 63a43b5..5b77f93 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/MarkLatePaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/MarkLatePaymentBuilderService.java
@@ -18,13 +18,11 @@
 import io.mifos.individuallending.api.v1.domain.workflow.Action;
 import io.mifos.individuallending.internal.repository.CaseParametersEntity;
 import io.mifos.individuallending.internal.service.DataContextOfAction;
-import io.mifos.individuallending.internal.service.DesignatorToAccountIdentifierMapper;
 import io.mifos.individuallending.internal.service.schedule.ScheduledAction;
 import io.mifos.individuallending.internal.service.schedule.ScheduledCharge;
 import io.mifos.individuallending.internal.service.schedule.ScheduledChargesService;
 import io.mifos.portfolio.api.v1.domain.ChargeDefinition;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
-import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -43,25 +41,19 @@
 @Service
 public class MarkLatePaymentBuilderService implements PaymentBuilderService {
   private final ScheduledChargesService scheduledChargesService;
-  private final AccountingAdapter accountingAdapter;
 
   @Autowired
-  public MarkLatePaymentBuilderService(
-      final ScheduledChargesService scheduledChargesService,
-      final AccountingAdapter accountingAdapter) {
+  public MarkLatePaymentBuilderService(final ScheduledChargesService scheduledChargesService) {
     this.scheduledChargesService = scheduledChargesService;
-    this.accountingAdapter = accountingAdapter;
   }
 
+  @Override
   public PaymentBuilder getPaymentBuilder(
       final @Nonnull DataContextOfAction dataContextOfAction,
       final @Nullable BigDecimal ignored,
-      final LocalDate forDate)
+      final LocalDate forDate,
+      final RunningBalances runningBalances)
   {
-    final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
-        = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
-    final RunningBalances runningBalances = new RealRunningBalances(accountingAdapter, designatorToAccountIdentifierMapper);
-
     final LocalDate startOfTerm = runningBalances.getStartOfTermOrThrow(dataContextOfAction);
 
     final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/OpenPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/OpenPaymentBuilderService.java
index a1e05c6..d4da18c 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/OpenPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/OpenPaymentBuilderService.java
@@ -45,7 +45,8 @@
   public PaymentBuilder getPaymentBuilder(
       final DataContextOfAction dataContextOfAction,
       final BigDecimal ignored,
-      final LocalDate forDate)
+      final LocalDate forDate,
+      final RunningBalances runningBalances)
   {
     final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
     final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderService.java
index e3e44df..b5b1c61 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderService.java
@@ -5,7 +5,6 @@
 import io.mifos.portfolio.api.v1.domain.CostComponent;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import java.math.BigDecimal;
 import java.time.LocalDate;
 
@@ -23,6 +22,7 @@
 
   PaymentBuilder getPaymentBuilder(
       final @Nonnull DataContextOfAction dataContextOfAction,
-      final @Nullable BigDecimal requestedDisbursalSize,
-      final LocalDate forDate);
+      final BigDecimal forPaymentSize,
+      final LocalDate forDate,
+      final @Nonnull RunningBalances runningBalances);
 }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RealRunningBalances.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RealRunningBalances.java
index a026f4a..05dc2f6 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RealRunningBalances.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RealRunningBalances.java
@@ -39,7 +39,7 @@
   private final AccountingAdapter accountingAdapter;
   private final ExpiringMap<String, BigDecimal> realAccountBalanceCache;
 
-  RealRunningBalances(
+  public RealRunningBalances(
       final AccountingAdapter accountingAdapter,
       final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper) {
     this.accountingAdapter = accountingAdapter;
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RecoverPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RecoverPaymentBuilderService.java
index e3b01a6..2ca9814 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RecoverPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RecoverPaymentBuilderService.java
@@ -29,7 +29,11 @@
 @Service
 public class RecoverPaymentBuilderService implements PaymentBuilderService {
   @Override
-  public PaymentBuilder getPaymentBuilder(@Nonnull DataContextOfAction dataContextOfAction, @Nullable BigDecimal requestedDisbursalSize, LocalDate forDate) {
+  public PaymentBuilder getPaymentBuilder(
+      final @Nonnull DataContextOfAction dataContextOfAction,
+      final @Nullable BigDecimal requestedDisbursalSize,
+      final LocalDate forDate,
+      final RunningBalances runningBalances) {
     return null;
   }
 }
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/WriteOffPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/WriteOffPaymentBuilderService.java
index 8d392b9..41faf76 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/WriteOffPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/WriteOffPaymentBuilderService.java
@@ -29,7 +29,12 @@
 @Service
 public class WriteOffPaymentBuilderService implements PaymentBuilderService {
   @Override
-  public PaymentBuilder getPaymentBuilder(@Nonnull DataContextOfAction dataContextOfAction, @Nullable BigDecimal requestedDisbursalSize, LocalDate forDate) {
+  public PaymentBuilder getPaymentBuilder(
+      final @Nonnull DataContextOfAction dataContextOfAction,
+      final @Nullable BigDecimal ignored,
+      final LocalDate forDate,
+      final RunningBalances runningBalances)
+  {
     return null;
   }
 }
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 a787475..b3f1424 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
@@ -3,25 +3,11 @@
 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.repository.CaseParametersEntity;
-import io.mifos.individuallending.internal.service.DataContextOfAction;
-import io.mifos.individuallending.internal.service.DefaultChargeDefinitionsMocker;
-import io.mifos.individuallending.internal.service.schedule.ScheduledChargesService;
 import io.mifos.portfolio.api.v1.domain.CostComponent;
 import io.mifos.portfolio.api.v1.domain.Payment;
-import io.mifos.portfolio.service.internal.repository.BalanceSegmentRepository;
-import io.mifos.portfolio.service.internal.repository.CaseEntity;
-import io.mifos.portfolio.service.internal.repository.ProductEntity;
-import io.mifos.portfolio.service.internal.service.ChargeDefinitionService;
-import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import org.junit.Assert;
 import org.junit.Test;
-import org.mockito.Mockito;
 
-import java.math.BigDecimal;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
 import java.util.Collections;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -29,51 +15,20 @@
 public class AcceptPaymentBuilderServiceTest {
   @Test
   public void getPaymentBuilder() throws Exception {
-    final LocalDate startOfTerm = LocalDate.of(2015, 1, 15);
-    final LocalDateTime endOfTerm = LocalDate.of(2015, 8, 15).atStartOfDay();
-    final LocalDate forDate = startOfTerm.plusMonths(1);
-    final BigDecimal paymentSize = BigDecimal.valueOf(100_00, 2);
-    final BigDecimal balance = BigDecimal.valueOf(2000_00, 2);
-    final BigDecimal balanceRangeMaximum = BigDecimal.valueOf(1000_00, 2);
-    final BigDecimal accruedInterest = BigDecimal.valueOf(10_00, 2);
+    final PaymentBuilderServiceTestCase testCase = new PaymentBuilderServiceTestCase("simple case");
+    testCase.runningBalances.adjustBalance(AccountDesignators.CUSTOMER_LOAN_PRINCIPAL, testCase.balance.negate());
+    testCase.runningBalances.adjustBalance(AccountDesignators.INTEREST_ACCRUAL, testCase.accruedInterest);
 
-    final BalanceSegmentRepository balanceSegmentRepository = Mockito.mock(BalanceSegmentRepository.class);
-    final ChargeDefinitionService chargeDefinitionService = DefaultChargeDefinitionsMocker.getChargeDefinitionService(Collections.emptyList());
-    final ScheduledChargesService scheduledChargesService = new ScheduledChargesService(chargeDefinitionService, balanceSegmentRepository);
-    final AccountingAdapter accountingAdapter = Mockito.mock(AccountingAdapter.class);
-    final AcceptPaymentBuilderService testSubject = new AcceptPaymentBuilderService(
-        scheduledChargesService,
-        accountingAdapter);
-    final SimulatedRunningBalances runningBalances  = new SimulatedRunningBalances(startOfTerm);
-    runningBalances.adjustBalance(AccountDesignators.CUSTOMER_LOAN_PRINCIPAL, balance.negate());
-    runningBalances.adjustBalance(AccountDesignators.INTEREST_ACCRUAL, accruedInterest);
+    final PaymentBuilder paymentBuilder = PaymentBuilderServiceTestHarness.constructCallToPaymentBuilder(
+        AcceptPaymentBuilderService::new, testCase);
 
-
-    final ProductEntity product = new ProductEntity();
-    product.setIdentifier("blah");
-    product.setMinorCurrencyUnitDigits(2);
-    final CaseEntity customerCase = new CaseEntity();
-    customerCase.setEndOfTerm(endOfTerm);
-    final CaseParametersEntity caseParameters = new CaseParametersEntity();
-    caseParameters.setPaymentSize(paymentSize);
-    caseParameters.setBalanceRangeMaximum(balanceRangeMaximum);
-    caseParameters.setPaymentCyclePeriod(1);
-    caseParameters.setPaymentCycleTemporalUnit(ChronoUnit.MONTHS);
-    caseParameters.setCreditWorthinessFactors(Collections.emptySet());
-
-    final DataContextOfAction dataContextOfAction = new DataContextOfAction(product, customerCase, caseParameters, Collections.emptyList());
-    final PaymentBuilder paymentBuilder = testSubject.getPaymentBuilderHelper(
-        dataContextOfAction,
-        paymentSize,
-        forDate,
-        runningBalances);
     final Payment payment = paymentBuilder.buildPayment(Action.ACCEPT_PAYMENT, Collections.emptySet());
     Assert.assertNotNull(payment);
     final Map<String, CostComponent> mappedCostComponents = payment.getCostComponents().stream()
         .collect(Collectors.toMap(CostComponent::getChargeIdentifier, x -> x));
 
-    Assert.assertEquals(accruedInterest, mappedCostComponents.get(ChargeIdentifiers.INTEREST_ID).getAmount());
-    Assert.assertEquals(accruedInterest, mappedCostComponents.get(ChargeIdentifiers.REPAY_INTEREST_ID).getAmount());
-    Assert.assertEquals(paymentSize.subtract(accruedInterest), mappedCostComponents.get(ChargeIdentifiers.REPAY_PRINCIPAL_ID).getAmount());
+    Assert.assertEquals(testCase.accruedInterest, mappedCostComponents.get(ChargeIdentifiers.INTEREST_ID).getAmount());
+    Assert.assertEquals(testCase.accruedInterest, mappedCostComponents.get(ChargeIdentifiers.REPAY_INTEREST_ID).getAmount());
+    Assert.assertEquals(testCase.paymentSize.subtract(testCase.accruedInterest), mappedCostComponents.get(ChargeIdentifiers.REPAY_PRINCIPAL_ID).getAmount());
   }
 }
\ No newline at end of file
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
new file mode 100644
index 0000000..5b56481
--- /dev/null
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderServiceTest.java
@@ -0,0 +1,32 @@
+package io.mifos.individuallending.internal.service.costcomponent;
+
+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.portfolio.api.v1.domain.CostComponent;
+import io.mifos.portfolio.api.v1.domain.Payment;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class ApplyInterestPaymentBuilderServiceTest {
+  @Test
+  public void getPaymentBuilder() throws Exception {
+    final PaymentBuilderServiceTestCase testCase = new PaymentBuilderServiceTestCase("simple case");
+    testCase.runningBalances.adjustBalance(AccountDesignators.CUSTOMER_LOAN_PRINCIPAL, testCase.balance.negate());
+
+    final PaymentBuilder paymentBuilder = PaymentBuilderServiceTestHarness.constructCallToPaymentBuilder(
+        ApplyInterestPaymentBuilderService::new, testCase);
+
+    final Payment payment = paymentBuilder.buildPayment(Action.APPLY_INTEREST, Collections.emptySet());
+    Assert.assertNotNull(payment);
+    final Map<String, CostComponent> mappedCostComponents = payment.getCostComponents().stream()
+        .collect(Collectors.toMap(CostComponent::getChargeIdentifier, x -> x));
+
+    Assert.assertEquals(BigDecimal.valueOf(27, 2), mappedCostComponents.get(ChargeIdentifiers.INTEREST_ID).getAmount());
+  }
+}
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderServiceTestCase.java b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderServiceTestCase.java
new file mode 100644
index 0000000..acc5ded
--- /dev/null
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderServiceTestCase.java
@@ -0,0 +1,70 @@
+package io.mifos.individuallending.internal.service.costcomponent;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+class PaymentBuilderServiceTestCase {
+  private final String description;
+  private LocalDate startOfTerm = LocalDate.of(2015, 1, 15);
+  LocalDateTime endOfTerm = LocalDate.of(2015, 8, 15).atStartOfDay();
+  LocalDate forDate = startOfTerm.plusMonths(1);
+  BigDecimal paymentSize = BigDecimal.valueOf(100_00, 2);
+  BigDecimal balance = BigDecimal.valueOf(2000_00, 2);
+  BigDecimal balanceRangeMaximum = BigDecimal.valueOf(1000_00, 2);
+  BigDecimal interestRate = BigDecimal.valueOf(5_00, 2);
+  BigDecimal accruedInterest = BigDecimal.valueOf(10_00, 2);
+  SimulatedRunningBalances runningBalances;
+
+  PaymentBuilderServiceTestCase(final String description) {
+    this.description = description;
+    runningBalances = new SimulatedRunningBalances(startOfTerm);
+  }
+
+  PaymentBuilderServiceTestCase endOfTerm(LocalDateTime endOfTerm) {
+    this.endOfTerm = endOfTerm;
+    return this;
+  }
+
+  PaymentBuilderServiceTestCase forDate(LocalDate forDate) {
+    this.forDate = forDate;
+    return this;
+  }
+
+  PaymentBuilderServiceTestCase paymentSize(BigDecimal paymentSize) {
+    this.paymentSize = paymentSize;
+    return this;
+  }
+
+  PaymentBuilderServiceTestCase balance(BigDecimal balance) {
+    this.balance = balance;
+    return this;
+  }
+
+  PaymentBuilderServiceTestCase balanceRangeMaximum(BigDecimal balanceRangeMaximum) {
+    this.balanceRangeMaximum = balanceRangeMaximum;
+    return this;
+  }
+
+  PaymentBuilderServiceTestCase interestRate(BigDecimal interestRate) {
+    this.interestRate = interestRate;
+    return this;
+  }
+
+  PaymentBuilderServiceTestCase accruedInterest(BigDecimal accruedInterest) {
+    this.accruedInterest = accruedInterest;
+    return this;
+  }
+
+  PaymentBuilderServiceTestCase runningBalances(SimulatedRunningBalances newVal) {
+    this.runningBalances = newVal;
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    return "PaymentBuilderServiceTestCase{" +
+        "description='" + description + '\'' +
+        '}';
+  }
+}
\ No newline at end of file
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderServiceTestHarness.java b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderServiceTestHarness.java
new file mode 100644
index 0000000..c26126e
--- /dev/null
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/PaymentBuilderServiceTestHarness.java
@@ -0,0 +1,50 @@
+package io.mifos.individuallending.internal.service.costcomponent;
+
+import io.mifos.individuallending.internal.repository.CaseParametersEntity;
+import io.mifos.individuallending.internal.service.DataContextOfAction;
+import io.mifos.individuallending.internal.service.DefaultChargeDefinitionsMocker;
+import io.mifos.individuallending.internal.service.schedule.ScheduledChargesService;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentRepository;
+import io.mifos.portfolio.service.internal.repository.CaseEntity;
+import io.mifos.portfolio.service.internal.repository.ProductEntity;
+import io.mifos.portfolio.service.internal.service.ChargeDefinitionService;
+import org.mockito.Mockito;
+
+import java.time.temporal.ChronoUnit;
+import java.util.Collections;
+import java.util.function.Function;
+
+class PaymentBuilderServiceTestHarness {
+  static PaymentBuilder constructCallToPaymentBuilder (
+      final Function<ScheduledChargesService, PaymentBuilderService> serviceFactory,
+      final PaymentBuilderServiceTestCase testCase) {
+    final BalanceSegmentRepository balanceSegmentRepository = Mockito.mock(BalanceSegmentRepository.class);
+    final ChargeDefinitionService chargeDefinitionService = DefaultChargeDefinitionsMocker.getChargeDefinitionService(Collections.emptyList());
+    final ScheduledChargesService scheduledChargesService = new ScheduledChargesService(chargeDefinitionService, balanceSegmentRepository);
+    final PaymentBuilderService testSubject = serviceFactory.apply(scheduledChargesService);
+
+    final ProductEntity product = new ProductEntity();
+    product.setIdentifier("blah");
+    product.setMinorCurrencyUnitDigits(2);
+    final CaseEntity customerCase = new CaseEntity();
+    customerCase.setEndOfTerm(testCase.endOfTerm);
+    customerCase.setInterest(testCase.interestRate);
+    final CaseParametersEntity caseParameters = new CaseParametersEntity();
+    caseParameters.setPaymentSize(testCase.paymentSize);
+    caseParameters.setBalanceRangeMaximum(testCase.balanceRangeMaximum);
+    caseParameters.setPaymentCyclePeriod(1);
+    caseParameters.setPaymentCycleTemporalUnit(ChronoUnit.MONTHS);
+    caseParameters.setCreditWorthinessFactors(Collections.emptySet());
+
+    final DataContextOfAction dataContextOfAction = new DataContextOfAction(
+        product,
+        customerCase,
+        caseParameters,
+        Collections.emptyList());
+    return testSubject.getPaymentBuilder(
+        dataContextOfAction,
+        testCase.paymentSize,
+        testCase.forDate,
+        testCase.runningBalances);
+  }
+}
\ No newline at end of file