Merge pull request #18 from myrle-krantz/develop

Modelled Teller Management use case in which the customer wishes to know which costs a payment will be applied to.
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/PlannedPayment.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/PlannedPayment.java
index f8c62fb..6157f37 100644
--- a/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/PlannedPayment.java
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/domain/caseinstance/PlannedPayment.java
@@ -15,6 +15,8 @@
  */
 package io.mifos.individuallending.api.v1.domain.caseinstance;
 
+import io.mifos.portfolio.api.v1.domain.CostComponent;
+
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.Objects;
@@ -24,57 +26,6 @@
  */
 @SuppressWarnings({"WeakerAccess", "unused"})
 public final class PlannedPayment {
-  static public final class CostComponent
-  {
-    private String chargeIdentifier;
-    private BigDecimal amount;
-
-    public CostComponent() {
-    }
-
-    public CostComponent(String chargeIdentifier, BigDecimal amount) {
-      this.chargeIdentifier = chargeIdentifier;
-      this.amount = amount;
-    }
-
-    public String getChargeIdentifier() {
-      return chargeIdentifier;
-    }
-
-    public void setChargeIdentifier(String chargeIdentifier) {
-      this.chargeIdentifier = chargeIdentifier;
-    }
-
-    public BigDecimal getAmount() {
-      return amount;
-    }
-
-    public void setAmount(BigDecimal amount) {
-      this.amount = amount;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-      CostComponent that = (CostComponent) o;
-      return Objects.equals(chargeIdentifier, that.chargeIdentifier) &&
-              Objects.equals(amount, that.amount);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(chargeIdentifier, amount);
-    }
-
-    @Override
-    public String toString() {
-      return "CostComponent{" +
-              "chargeIdentifier='" + chargeIdentifier + '\'' +
-              ", amount=" + amount +
-              '}';
-    }
-  }
   private Double interestRate;
   private List<CostComponent> costComponents;
   private BigDecimal remainingPrincipal;
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/workflow/AcceptPaymentParameters.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/workflow/AcceptPaymentParameters.java
deleted file mode 100644
index f5f5882..0000000
--- a/api/src/main/java/io/mifos/individuallending/api/v1/domain/workflow/AcceptPaymentParameters.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2017 The Mifos Initiative.
- *
- * 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.api.v1.domain.workflow;
-
-/**
- * @author Myrle Krantz
- */
-public class AcceptPaymentParameters {
-}
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/workflow/DisburseParameters.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/workflow/DisburseParameters.java
deleted file mode 100644
index 16483cd..0000000
--- a/api/src/main/java/io/mifos/individuallending/api/v1/domain/workflow/DisburseParameters.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2017 The Mifos Initiative.
- *
- * 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.api.v1.domain.workflow;
-
-/**
- * @author Myrle Krantz
- */
-public class DisburseParameters {
-}
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/client/PortfolioManager.java b/api/src/main/java/io/mifos/portfolio/api/v1/client/PortfolioManager.java
index 8e11724..cd02791 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/client/PortfolioManager.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/client/PortfolioManager.java
@@ -246,26 +246,27 @@
                                 @PathVariable("caseidentifier") final String caseIdentifier);
 
   @RequestMapping(
-          value = "/products/{productidentifier}/cases/{caseidentifier}/commands/",
+          value = "/products/{productidentifier}/cases/{caseidentifier}/actions/{actionidentifier}/costcomponents",
+          method = RequestMethod.GET,
+          produces = MediaType.ALL_VALUE,
+          consumes = MediaType.APPLICATION_JSON_VALUE
+  )
+  List<CostComponent> getCostComponentsForAction(@PathVariable("productidentifier") final String productIdentifier,
+                                                 @PathVariable("caseidentifier") final String caseIdentifier,
+                                                 @PathVariable("actionidentifier") final String actionIdentifier);
+
+  @RequestMapping(
+          value = "/products/{productidentifier}/cases/{caseidentifier}/commands/{actionidentifier}",
           method = RequestMethod.POST,
           produces = MediaType.APPLICATION_JSON_VALUE,
           consumes = MediaType.APPLICATION_JSON_VALUE
   )
   void executeCaseCommand(@PathVariable("productidentifier") final String productIdentifier,
                           @PathVariable("caseidentifier") final String caseIdentifier,
+                          @PathVariable("actionidentifier") final String actionIdentifier,
                           final Command command);
 
   @RequestMapping(
-          value = "/products/{productidentifier}/cases/{caseidentifier}/commands/{commandidentifier}",
-          method = RequestMethod.GET,
-          produces = MediaType.ALL_VALUE,
-          consumes = MediaType.APPLICATION_JSON_VALUE
-  )
-  Command getExecutedCommandForCase(@PathVariable("productidentifier") final String productIdentifier,
-                                    @PathVariable("caseidentifier") final String caseIdentifier,
-                                    @PathVariable("commandidentifier") final String commandIdentifier);
-
-  @RequestMapping(
           value = "/products/{productidentifier}/cases/{caseidentifier}/tasks/",
           method = RequestMethod.GET,
           produces = MediaType.ALL_VALUE,
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/domain/Command.java b/api/src/main/java/io/mifos/portfolio/api/v1/domain/Command.java
index 198f1c1..4cb0cc6 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/domain/Command.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/domain/Command.java
@@ -17,14 +17,14 @@
 
 import org.hibernate.validator.constraints.NotBlank;
 
+import java.util.List;
+
 /**
  * @author Myrle Krantz
  */
 @SuppressWarnings({"WeakerAccess", "unused"})
 public final class Command {
-  @NotBlank
-  private String action;
-  private String parameters;
+  private List<AccountAssignment> oneTimeAccountAssignments;
   private String comment;
   private String createdOn;
   private String createdBy;
@@ -32,27 +32,6 @@
   public Command() {
   }
 
-  public Command(String action, String parameters) {
-    this.action = action;
-    this.parameters = parameters;
-  }
-
-  public String getAction() {
-    return action;
-  }
-
-  public void setAction(String action) {
-    this.action = action;
-  }
-
-  public String getParameters() {
-    return parameters;
-  }
-
-  public void setParameters(String parameters) {
-    this.parameters = parameters;
-  }
-
   public String getComment() {
     return comment;
   }
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/domain/CostComponent.java b/api/src/main/java/io/mifos/portfolio/api/v1/domain/CostComponent.java
new file mode 100644
index 0000000..058aade
--- /dev/null
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/domain/CostComponent.java
@@ -0,0 +1,59 @@
+package io.mifos.portfolio.api.v1.domain;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+public class CostComponent {
+  private String chargeIdentifier;
+  private BigDecimal amount;
+
+  public CostComponent() {
+  }
+
+  public CostComponent(String chargeIdentifier, BigDecimal amount) {
+    this.chargeIdentifier = chargeIdentifier;
+    this.amount = amount;
+  }
+
+  public String getChargeIdentifier() {
+    return chargeIdentifier;
+  }
+
+  public void setChargeIdentifier(String chargeIdentifier) {
+    this.chargeIdentifier = chargeIdentifier;
+  }
+
+  public BigDecimal getAmount() {
+    return amount;
+  }
+
+  public void setAmount(BigDecimal amount) {
+    this.amount = amount;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CostComponent that = (CostComponent) o;
+    return Objects.equals(chargeIdentifier, that.chargeIdentifier) &&
+            Objects.equals(amount, that.amount);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(chargeIdentifier, amount);
+  }
+
+  @Override
+  public String toString() {
+    return "CostComponent{" +
+            "chargeIdentifier='" + chargeIdentifier + '\'' +
+            ", amount=" + amount +
+            '}';
+  }
+}
\ No newline at end of file
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestCommands.java b/component-test/src/main/java/io/mifos/portfolio/TestCommands.java
index 2485ae3..963f499 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestCommands.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestCommands.java
@@ -106,8 +106,8 @@
                                  final Action action,
                                  final String event,
                                  final Case.State nextState) throws InterruptedException {
-    final Command command = new Command(action.name(), null);
-    portfolioManager.executeCaseCommand(productIdentifier, caseIdentifier, command);
+    final Command command = new Command();
+    portfolioManager.executeCaseCommand(productIdentifier, caseIdentifier, action.name(), command);
 
     Assert.assertTrue(eventRecorder.waitForMatch(event,
             (IndividualLoanCommandEvent x) -> individualLoanCommandEventMatches(x, productIdentifier, caseIdentifier)));
@@ -121,9 +121,9 @@
                                  final Action action,
                                  final String event,
                                  final Case.State initialState) throws InterruptedException {
-    final Command command = new Command(action.name(), null);
+    final Command command = new Command();
     try {
-      portfolioManager.executeCaseCommand(productIdentifier, caseIdentifier, command);
+      portfolioManager.executeCaseCommand(productIdentifier, caseIdentifier, action.name(), command);
       Assert.fail();
     }
     catch (final IllegalArgumentException ignored) {}
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingCommandDispatcher.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingCommandDispatcher.java
index b9eb9b5..5b685b3 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingCommandDispatcher.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingCommandDispatcher.java
@@ -38,8 +38,8 @@
   }
 
   @Override
-  public void dispatch(final String productIdentifier, final String caseIdentifier, final Command command) {
-    final Action action = Action.valueOf(command.getAction());
+  public void dispatch(final String productIdentifier, final String caseIdentifier, final String actionIdentifier, final Command command) {
+    final Action action = Action.valueOf(actionIdentifier);
     switch (action) {
       case OPEN:
         this.commandGateway.process(new OpenCommand(productIdentifier, caseIdentifier));
@@ -66,7 +66,7 @@
         this.commandGateway.process(new RecoverCommand(productIdentifier, caseIdentifier));
         break;
       default:
-        throw ServiceException.badRequest("Action " + command.getAction() + " cannot be taken from current state.");
+        throw ServiceException.badRequest("Action ''{0}'' cannot be taken from current state.", actionIdentifier);
     }
   }
 }
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 8f5a0b7..7b70948 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
@@ -15,6 +15,7 @@
  */
 package io.mifos.individuallending.internal.service;
 
+import io.mifos.portfolio.api.v1.domain.CostComponent;
 import io.mifos.portfolio.service.internal.service.ChargeDefinitionService;
 import io.mifos.portfolio.service.internal.service.ProductService;
 import io.mifos.core.lang.DateConverter;
@@ -181,14 +182,14 @@
     final List<PlannedPayment> plannedPayments = new ArrayList<>();
     for (final Period repaymentPeriod : sortedRepaymentPeriods)
     {
-      final Map<String, PlannedPayment.CostComponent> costComponentMap = new HashMap<>();
+      final Map<String, CostComponent> costComponentMap = new HashMap<>();
       final SortedSet<ScheduledCharge> scheduledChargesInPeriod = orderedScheduledChargesGroupedByPeriod.get(repaymentPeriod);
       for (final ScheduledCharge scheduledCharge : scheduledChargesInPeriod)
       {
-        final PlannedPayment.CostComponent costComponent = costComponentMap
+        final CostComponent costComponent = costComponentMap
                 .computeIfAbsent(scheduledCharge.getChargeDefinition().getIdentifier(),
                 chargeIdentifier -> {
-                  final PlannedPayment.CostComponent ret = new PlannedPayment.CostComponent();
+                  final CostComponent ret = new CostComponent();
                   ret.setChargeIdentifier(scheduledCharge.getChargeDefinition().getIdentifier());
                   ret.setAmount(BigDecimal.ZERO);
                   return ret;
@@ -210,7 +211,7 @@
     if (balance.compareTo(BigDecimal.ZERO) != 0)
     {
       final PlannedPayment lastPayment = plannedPayments.get(plannedPayments.size() - 1);
-      final Optional<PlannedPayment.CostComponent> lastPaymentPayment = lastPayment.getCostComponents().stream()
+      final Optional<CostComponent> lastPaymentPayment = lastPayment.getCostComponents().stream()
               .filter(x -> x.getChargeIdentifier().equals(PAYMENT_ID)).findAny();
       lastPaymentPayment.ifPresent(x -> {
         x.setAmount(x.getAmount().subtract(lastPayment.getRemainingPrincipal()));
diff --git a/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java b/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
index a72ec2b..22e5a75 100644
--- a/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
+++ b/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
@@ -24,6 +24,7 @@
 import io.mifos.portfolio.api.v1.domain.Case;
 import io.mifos.portfolio.api.v1.domain.CasePage;
 import io.mifos.portfolio.api.v1.domain.Command;
+import io.mifos.portfolio.api.v1.domain.CostComponent;
 import io.mifos.portfolio.service.internal.command.ChangeCaseCommand;
 import io.mifos.portfolio.service.internal.command.CreateCaseCommand;
 import io.mifos.portfolio.service.internal.service.CaseService;
@@ -36,6 +37,8 @@
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
+import java.util.Collections;
+import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 
@@ -176,22 +179,41 @@
 
   @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.CASE_MANAGEMENT)
   @RequestMapping(
-          value = "{caseidentifier}/commands/",
+          value = "{caseidentifier}/actions/{actionidentifier}/costcomponents",
+          method = RequestMethod.GET,
+          consumes = MediaType.ALL_VALUE,
+          produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  @ResponseBody
+  List<CostComponent> getCostComponentsForAction(@PathVariable("productidentifier") final String productIdentifier,
+                                                 @PathVariable("caseidentifier") final String caseIdentifier,
+                                                 @PathVariable("actionidentifier") final String actionIdentifier)
+  {
+    checkThatCaseExists(productIdentifier, caseIdentifier);
+
+    return Collections.emptyList();
+    //return caseService.getActionCostComponentsForCase(productIdentifier, caseIdentifier);
+  }
+
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.CASE_MANAGEMENT)
+  @RequestMapping(
+          value = "{caseidentifier}/commands/{actionidentifier}",
           method = RequestMethod.POST,
           produces = MediaType.APPLICATION_JSON_VALUE,
           consumes = MediaType.APPLICATION_JSON_VALUE
   )
   public @ResponseBody ResponseEntity<Void> executeCaseCommand(@PathVariable("productidentifier") final String productIdentifier,
                                                                @PathVariable("caseidentifier") final String caseIdentifier,
+                                                               @PathVariable("actionidentifier") final String actionIdentifier,
                                                                @RequestBody @Valid final Command command)
   {
     checkThatCaseExists(productIdentifier, caseIdentifier);
     final Set<String> nextActions = caseService.getNextActionsForCase(productIdentifier, caseIdentifier);
-    if (!nextActions.contains(command.getAction()))
-      throw ServiceException.badRequest("Action " + command.getAction() + " cannot be taken from current state.");
+    if (!nextActions.contains(actionIdentifier))
+      throw ServiceException.badRequest("Action " + actionIdentifier + " cannot be taken from current state.");
 
     final ProductCommandDispatcher productCommandDispatcher = caseService.getProductCommandDispatcher(productIdentifier);
-    productCommandDispatcher.dispatch(productIdentifier, caseIdentifier, command);
+    productCommandDispatcher.dispatch(productIdentifier, caseIdentifier, actionIdentifier, command);
 
     return new ResponseEntity<>(HttpStatus.ACCEPTED);
   }
diff --git a/service/src/main/java/io/mifos/products/spi/ProductCommandDispatcher.java b/service/src/main/java/io/mifos/products/spi/ProductCommandDispatcher.java
index f338c58..e5f0bd9 100644
--- a/service/src/main/java/io/mifos/products/spi/ProductCommandDispatcher.java
+++ b/service/src/main/java/io/mifos/products/spi/ProductCommandDispatcher.java
@@ -21,5 +21,5 @@
  * @author Myrle Krantz
  */
 public interface ProductCommandDispatcher {
-  void dispatch(String productIdentifier, String caseIdentifier, Command command);
+  void dispatch(String productIdentifier, String caseIdentifier, final String actionIdentifier, Command command);
 }