Merge pull request #7 from markusgeiss/develop

added opened on and last transaction date to product instance
diff --git a/api/src/main/java/io/mifos/deposit/api/v1/EventConstants.java b/api/src/main/java/io/mifos/deposit/api/v1/EventConstants.java
index 7e63d55..a10c48f 100644
--- a/api/src/main/java/io/mifos/deposit/api/v1/EventConstants.java
+++ b/api/src/main/java/io/mifos/deposit/api/v1/EventConstants.java
@@ -49,6 +49,7 @@
 
   String ACTIVATE_PRODUCT_INSTANCE_COMMAND = "ACTIVATE";
   String CLOSE_PRODUCT_INSTANCE_COMMAND = "CLOSE";
+  String PRODUCT_INSTANCE_TRANSACTION = "TRANSACTION";
 
   String DIVIDEND_DISTRIBUTION = "dividend-distribution";
   String SELECTOR_DIVIDEND_DISTRIBUTION = SELECTOR_NAME + " = '" + DIVIDEND_DISTRIBUTION + "'";
diff --git a/api/src/main/java/io/mifos/deposit/api/v1/instance/domain/ProductInstance.java b/api/src/main/java/io/mifos/deposit/api/v1/instance/domain/ProductInstance.java
index 348543d..a37be73 100644
--- a/api/src/main/java/io/mifos/deposit/api/v1/instance/domain/ProductInstance.java
+++ b/api/src/main/java/io/mifos/deposit/api/v1/instance/domain/ProductInstance.java
@@ -28,6 +28,8 @@
   @ValidIdentifier(maxLength = 34, optional = true)
   private String accountIdentifier;
   private Set<String> beneficiaries;
+  private String openedOn;
+  private String lastTransactionDate;
   private String state;
   private Double balance;
 
@@ -67,6 +69,22 @@
     this.beneficiaries = beneficiaries;
   }
 
+  public String getOpenedOn() {
+    return this.openedOn;
+  }
+
+  public void setOpenedOn(final String openedOn) {
+    this.openedOn = openedOn;
+  }
+
+  public String getLastTransactionDate() {
+    return this.lastTransactionDate;
+  }
+
+  public void setLastTransactionDate(final String lastTransactionDate) {
+    this.lastTransactionDate = lastTransactionDate;
+  }
+
   public String getState() {
     return this.state;
   }
diff --git a/component-test/src/main/java/io/mifos/deposit/TestProductInstance.java b/component-test/src/main/java/io/mifos/deposit/TestProductInstance.java
index cb875e5..6018d94 100644
--- a/component-test/src/main/java/io/mifos/deposit/TestProductInstance.java
+++ b/component-test/src/main/java/io/mifos/deposit/TestProductInstance.java
@@ -90,17 +90,21 @@
     Assert.assertTrue(
         super.eventRecorder.wait(EventConstants.ACTIVATE_PRODUCT_INSTANCE,
             foundProductInstance.getAccountIdentifier()));
+
+    final ProductInstance activatedProductInstance = super.depositAccountManager.findProductInstance(foundProductInstance.getAccountIdentifier());
+    Assert.assertNotNull(activatedProductInstance.getOpenedOn());
   }
 
   @Test
   public void shouldCloseProductInstance() throws Exception {
     final ProductDefinition productDefinition = Fixture.productDefinition();
-
     super.depositAccountManager.create(productDefinition);
 
     super.eventRecorder.wait(EventConstants.POST_PRODUCT_DEFINITION, productDefinition.getIdentifier());
 
     final ProductInstance productInstance = Fixture.productInstance(productDefinition.getIdentifier());
+    final String openedOn = "2013-05-08Z";
+    productInstance.setOpenedOn(openedOn);
 
     super.depositAccountManager.create(productInstance);
 
@@ -110,6 +114,7 @@
     Assert.assertNotNull(productInstances);
     Assert.assertEquals(1, productInstances.size());
     final ProductInstance foundProductInstance = productInstances.get(0);
+    Assert.assertEquals(openedOn, foundProductInstance.getOpenedOn());
 
     super.depositAccountManager.postProductInstanceCommand(
         foundProductInstance.getAccountIdentifier(), EventConstants.CLOSE_PRODUCT_INSTANCE_COMMAND);
@@ -330,4 +335,44 @@
 
     Assert.assertTrue(expectedTransactionTypes.isEmpty());
   }
+
+  @Test
+  public void shouldAddTransactionDateProductInstance() throws Exception {
+    final ProductDefinition productDefinition = Fixture.productDefinition();
+
+    super.depositAccountManager.create(productDefinition);
+
+    super.eventRecorder.wait(EventConstants.POST_PRODUCT_DEFINITION, productDefinition.getIdentifier());
+
+    final ProductInstance productInstance = Fixture.productInstance(productDefinition.getIdentifier());
+
+    super.depositAccountManager.create(productInstance);
+
+    super.eventRecorder.wait(EventConstants.POST_PRODUCT_INSTANCE, productInstance.getCustomerIdentifier());
+
+    final List<ProductInstance> productInstances = super.depositAccountManager.findProductInstances(productDefinition.getIdentifier());
+    Assert.assertNotNull(productInstances);
+    Assert.assertEquals(1, productInstances.size());
+    final ProductInstance foundProductInstance = productInstances.get(0);
+
+    super.depositAccountManager.postProductInstanceCommand(
+        foundProductInstance.getAccountIdentifier(), EventConstants.ACTIVATE_PRODUCT_INSTANCE_COMMAND);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.ACTIVATE_PRODUCT_INSTANCE,
+            foundProductInstance.getAccountIdentifier()));
+
+    super.depositAccountManager.postProductInstanceCommand(
+        foundProductInstance.getAccountIdentifier(), EventConstants.PRODUCT_INSTANCE_TRANSACTION);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.PUT_PRODUCT_INSTANCE,
+            foundProductInstance.getAccountIdentifier()));
+
+    final List<ProductInstance> transactedProductInstances = super.depositAccountManager.findProductInstances(productDefinition.getIdentifier());
+    Assert.assertNotNull(transactedProductInstances);
+    Assert.assertEquals(1, transactedProductInstances.size());
+    final ProductInstance transactedProductInstance = transactedProductInstances.get(0);
+    Assert.assertNotNull(transactedProductInstance.getLastTransactionDate());
+  }
 }
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/command/TransactionProcessedCommand.java b/service/src/main/java/io/mifos/deposit/service/internal/command/TransactionProcessedCommand.java
new file mode 100644
index 0000000..79950c9
--- /dev/null
+++ b/service/src/main/java/io/mifos/deposit/service/internal/command/TransactionProcessedCommand.java
@@ -0,0 +1,30 @@
+/*
+ * 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.deposit.service.internal.command;
+
+public class TransactionProcessedCommand {
+
+  private final String accountIdentifier;
+
+  public TransactionProcessedCommand(final String accountIdentifier) {
+    super();
+    this.accountIdentifier = accountIdentifier;
+  }
+
+  public String accountIdentifier() {
+    return this.accountIdentifier;
+  }
+}
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/command/handler/ProductInstanceAggregate.java b/service/src/main/java/io/mifos/deposit/service/internal/command/handler/ProductInstanceAggregate.java
index da6b8d7..29f188c 100644
--- a/service/src/main/java/io/mifos/deposit/service/internal/command/handler/ProductInstanceAggregate.java
+++ b/service/src/main/java/io/mifos/deposit/service/internal/command/handler/ProductInstanceAggregate.java
@@ -27,6 +27,7 @@
 import io.mifos.deposit.service.internal.command.ActivateProductInstanceCommand;
 import io.mifos.deposit.service.internal.command.CloseProductInstanceCommand;
 import io.mifos.deposit.service.internal.command.CreateProductInstanceCommand;
+import io.mifos.deposit.service.internal.command.TransactionProcessedCommand;
 import io.mifos.deposit.service.internal.command.UpdateProductInstanceCommand;
 import io.mifos.deposit.service.internal.mapper.ProductInstanceMapper;
 import io.mifos.deposit.service.internal.repository.ProductDefinitionEntity;
@@ -41,6 +42,7 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.Clock;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -120,6 +122,9 @@
     if (optionalProductInstance.isPresent()) {
       final ProductInstanceEntity productInstanceEntity = optionalProductInstance.get();
       productInstanceEntity.setState("ACTIVE");
+      if (productInstanceEntity.getOpenedOn() == null) {
+        productInstanceEntity.setOpenedOn(LocalDate.now(Clock.systemUTC()));
+      }
       productInstanceEntity.setLastModifiedBy(UserContextHolder.checkedGetUser());
       productInstanceEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC()));
       this.productInstanceRepository.save(productInstanceEntity);
@@ -186,6 +191,23 @@
     }
   }
 
+  @Transactional
+  @CommandHandler
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PUT_PRODUCT_INSTANCE)
+  public String process(final TransactionProcessedCommand transactionProcessedCommand) {
+    final Optional<ProductInstanceEntity> optionalProductInstance =
+        this.productInstanceRepository.findByAccountIdentifier(transactionProcessedCommand.accountIdentifier());
+
+    final ProductInstanceEntity productInstanceEntity = optionalProductInstance.orElseThrow(() ->
+        ServiceException.notFound("Product instance {0} not found.", transactionProcessedCommand.accountIdentifier()));
+
+    productInstanceEntity.setLastTransactionDate(LocalDateTime.now(Clock.systemUTC()));
+    
+    this.productInstanceRepository.save(productInstanceEntity);
+
+    return transactionProcessedCommand.accountIdentifier();
+  }
+
   private boolean hasChanged(final ProductInstance productInstance, final ProductInstanceEntity productInstanceEntity) {
     if (productInstance.getBeneficiaries() != null) {
       if (productInstanceEntity.getBeneficiaries() == null) {
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/mapper/ProductInstanceMapper.java b/service/src/main/java/io/mifos/deposit/service/internal/mapper/ProductInstanceMapper.java
index 68a31c3..a841833 100644
--- a/service/src/main/java/io/mifos/deposit/service/internal/mapper/ProductInstanceMapper.java
+++ b/service/src/main/java/io/mifos/deposit/service/internal/mapper/ProductInstanceMapper.java
@@ -16,6 +16,7 @@
 package io.mifos.deposit.service.internal.mapper;
 
 import io.mifos.accounting.api.v1.domain.Account;
+import io.mifos.core.lang.DateConverter;
 import io.mifos.core.lang.ServiceException;
 import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
 import io.mifos.deposit.service.internal.repository.ProductDefinitionEntity;
@@ -49,6 +50,14 @@
             productInstance.getBeneficiaries().stream().collect(Collectors.joining(",")));
       }
 
+      if (productInstance.getOpenedOn() != null) {
+        productInstanceEntity.setOpenedOn(DateConverter.dateFromIsoString(productInstance.getOpenedOn()));
+      }
+
+      if (productInstance.getLastTransactionDate() != null) {
+        productInstanceEntity.setLastTransactionDate(DateConverter.fromIsoString(productInstance.getLastTransactionDate()));
+      }
+
       return productInstanceEntity;
     } else {
       throw ServiceException.notFound("Unable to assign product {0} to customer {1}, product not found.",
@@ -69,6 +78,14 @@
       ));
     }
 
+    if (productInstanceEntity.getOpenedOn() != null) {
+      productInstance.setOpenedOn(DateConverter.toIsoString(productInstanceEntity.getOpenedOn()));
+    }
+
+    if (productInstanceEntity.getLastTransactionDate() != null) {
+      productInstance.setLastTransactionDate(DateConverter.toIsoString(productInstanceEntity.getLastTransactionDate()));
+    }
+
     if (account != null && account.getBalance() != null) {
       productInstance.setBalance(account.getBalance());
     } else {
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/repository/ProductInstanceEntity.java b/service/src/main/java/io/mifos/deposit/service/internal/repository/ProductInstanceEntity.java
index 53ad0bc..f95a8ea 100644
--- a/service/src/main/java/io/mifos/deposit/service/internal/repository/ProductInstanceEntity.java
+++ b/service/src/main/java/io/mifos/deposit/service/internal/repository/ProductInstanceEntity.java
@@ -15,6 +15,7 @@
  */
 package io.mifos.deposit.service.internal.repository;
 
+import io.mifos.core.mariadb.util.LocalDateConverter;
 import io.mifos.core.mariadb.util.LocalDateTimeConverter;
 
 import javax.persistence.CascadeType;
@@ -28,6 +29,7 @@
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 
 @Entity
@@ -47,6 +49,12 @@
   private String accountIdentifier;
   @Column(name = "beneficiaries", nullable = true, length = 256)
   private String beneficiaries;
+  @Convert(converter = LocalDateConverter.class)
+  @Column(name = "opened_on", nullable = false)
+  private LocalDate openedOn;
+  @Convert(converter = LocalDateTimeConverter.class)
+  @Column(name = "last_transaction_date", nullable = false)
+  private LocalDateTime lastTransactionDate;
   @Column(name = "a_state", nullable = false)
   private String state;
   @Column(name = "created_by", nullable = false, length = 32)
@@ -104,6 +112,22 @@
     this.beneficiaries = beneficiaries;
   }
 
+  public LocalDate getOpenedOn() {
+    return this.openedOn;
+  }
+
+  public void setOpenedOn(final LocalDate openedOn) {
+    this.openedOn = openedOn;
+  }
+
+  public LocalDateTime getLastTransactionDate() {
+    return this.lastTransactionDate;
+  }
+
+  public void setLastTransactionDate(final LocalDateTime lastTransactionDate) {
+    this.lastTransactionDate = lastTransactionDate;
+  }
+
   public String getState() {
     return this.state;
   }
diff --git a/service/src/main/java/io/mifos/deposit/service/rest/ProductInstanceRestController.java b/service/src/main/java/io/mifos/deposit/service/rest/ProductInstanceRestController.java
index 0d0deb4..e56bda8 100644
--- a/service/src/main/java/io/mifos/deposit/service/rest/ProductInstanceRestController.java
+++ b/service/src/main/java/io/mifos/deposit/service/rest/ProductInstanceRestController.java
@@ -27,6 +27,7 @@
 import io.mifos.deposit.service.internal.command.ActivateProductInstanceCommand;
 import io.mifos.deposit.service.internal.command.CloseProductInstanceCommand;
 import io.mifos.deposit.service.internal.command.CreateProductInstanceCommand;
+import io.mifos.deposit.service.internal.command.TransactionProcessedCommand;
 import io.mifos.deposit.service.internal.command.UpdateProductInstanceCommand;
 import io.mifos.deposit.service.internal.service.ProductInstanceService;
 import org.slf4j.Logger;
@@ -150,6 +151,9 @@
       case EventConstants.CLOSE_PRODUCT_INSTANCE_COMMAND:
         this.commandGateway.process(new CloseProductInstanceCommand(identifier));
         break;
+      case EventConstants.PRODUCT_INSTANCE_TRANSACTION:
+        this.commandGateway.process(new TransactionProcessedCommand(identifier));
+        break;
       default:
         throw ServiceException.badRequest("Unsupported command {0}", command);
     }
diff --git a/service/src/main/resources/db/migrations/mariadb/V6__add_opened_on_last_transaction_to_instance.sql b/service/src/main/resources/db/migrations/mariadb/V6__add_opened_on_last_transaction_to_instance.sql
new file mode 100644
index 0000000..89e2bef
--- /dev/null
+++ b/service/src/main/resources/db/migrations/mariadb/V6__add_opened_on_last_transaction_to_instance.sql
@@ -0,0 +1,20 @@
+--
+-- 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.
+--
+
+ALTER TABLE shed_product_instances ADD COLUMN opened_on DATE NULL;
+ALTER TABLE shed_product_instances ADD COLUMN last_transaction_date TIMESTAMP(3) NULL;
+
+UPDATE shed_product_instances set opened_on = CURDATE() WHERE a_state <> 'PENDING';
\ No newline at end of file