added ability to activate and close product instances
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 f6c20a3..81afd39 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
@@ -36,4 +36,11 @@
   String SELECTOR_POST_PRODUCT_INSTANCE = SELECTOR_NAME + " = '" + POST_PRODUCT_INSTANCE + "'";
   String POST_PRODUCT_INSTANCE_STATE_CHANGE = "post-product-instance-state-change";
   String SELECTOR_POST_PRODUCT_INSTANCE_STATE_CHANGE = SELECTOR_NAME + " = '" + POST_PRODUCT_INSTANCE_STATE_CHANGE + "'";
+  String ACTIVATE_PRODUCT_INSTANCE = "activate-process-instance";
+  String SELECTOR_ACTIVATE_PRODUCT_INSTANCE = SELECTOR_NAME + " = '" + ACTIVATE_PRODUCT_INSTANCE + "'";
+  String CLOSE_PRODUCT_INSTANCE = "close-process-instance";
+  String SELECTOR_CLOSE_PRODUCT_INSTANCE = SELECTOR_NAME + " = '" + CLOSE_PRODUCT_INSTANCE + "'";
+
+  String ACTIVATE_PRODUCT_INSTANCE_COMMAND = "ACTIVATE";
+  String CLOSE_PRODUCT_INSTANCE_COMMAND = "CLOSE";
 }
diff --git a/api/src/main/java/io/mifos/deposit/api/v1/client/DepositAccountManager.java b/api/src/main/java/io/mifos/deposit/api/v1/client/DepositAccountManager.java
index 2cc93d4..cf34325 100644
--- a/api/src/main/java/io/mifos/deposit/api/v1/client/DepositAccountManager.java
+++ b/api/src/main/java/io/mifos/deposit/api/v1/client/DepositAccountManager.java
@@ -104,7 +104,7 @@
                @RequestBody @Valid final ProductDefinitionCommand command);
 
   @RequestMapping(
-      value = "/{identifier}/commands",
+      value = "/definitions/{identifier}/commands",
       method = RequestMethod.GET,
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.ALL_VALUE
@@ -126,4 +126,13 @@
       produces = MediaType.ALL_VALUE
   )
   List<ProductInstance> fetchProductInstances(@RequestParam(value = "customer", required = true) final String customer);
+
+  @RequestMapping(
+      value = "/instances/{identifier}",
+      method = RequestMethod.POST,
+      consumes = MediaType.APPLICATION_JSON_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  void postProductInstanceCommand(@PathVariable("identifier") final String identifier,
+                                  @RequestParam(value = "command", required = true) final String command);
 }
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 34b4682..e11817a 100644
--- a/component-test/src/main/java/io/mifos/deposit/TestProductInstance.java
+++ b/component-test/src/main/java/io/mifos/deposit/TestProductInstance.java
@@ -54,4 +54,64 @@
     Assert.assertEquals(productDefinition.getEquityLedgerIdentifier() + "." + customerIdentifier + ".00001",
         foundProductInstance.getAccountIdentifier());
   }
+
+  @Test
+  public void shouldActivateProductInstance() throws Exception {
+    final String customerIdentifier = "08154712";
+    final ProductDefinition productDefinition = Fixture.productDefinition();
+
+    super.depositAccountManager.create(productDefinition);
+
+    super.eventRecorder.wait(EventConstants.POST_PRODUCT_DEFINITION, productDefinition.getIdentifier());
+
+    final ProductInstance productInstance = new ProductInstance();
+    productInstance.setProductIdentifier(productDefinition.getIdentifier());
+    productInstance.setCustomerIdentifier(customerIdentifier);
+
+    super.depositAccountManager.create(productInstance);
+
+    super.eventRecorder.wait(EventConstants.POST_PRODUCT_INSTANCE, customerIdentifier);
+
+    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()));
+  }
+
+  @Test
+  public void shouldCloseProductInstance() throws Exception {
+    final String customerIdentifier = "08154713";
+    final ProductDefinition productDefinition = Fixture.productDefinition();
+
+    super.depositAccountManager.create(productDefinition);
+
+    super.eventRecorder.wait(EventConstants.POST_PRODUCT_DEFINITION, productDefinition.getIdentifier());
+
+    final ProductInstance productInstance = new ProductInstance();
+    productInstance.setProductIdentifier(productDefinition.getIdentifier());
+    productInstance.setCustomerIdentifier(customerIdentifier);
+
+    super.depositAccountManager.create(productInstance);
+
+    super.eventRecorder.wait(EventConstants.POST_PRODUCT_INSTANCE, customerIdentifier);
+
+    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.CLOSE_PRODUCT_INSTANCE_COMMAND);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.CLOSE_PRODUCT_INSTANCE,
+            foundProductInstance.getAccountIdentifier()));
+  }
 }
diff --git a/component-test/src/main/java/io/mifos/deposit/listener/ProductInstanceEventListener.java b/component-test/src/main/java/io/mifos/deposit/listener/ProductInstanceEventListener.java
index 94b36c9..2cdf9e3 100644
--- a/component-test/src/main/java/io/mifos/deposit/listener/ProductInstanceEventListener.java
+++ b/component-test/src/main/java/io/mifos/deposit/listener/ProductInstanceEventListener.java
@@ -50,4 +50,26 @@
     this.logger.debug("Product instance created.");
     this.eventRecorder.event(tenant, EventConstants.POST_PRODUCT_INSTANCE, payload, String.class);
   }
+
+  @JmsListener(
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_ACTIVATE_PRODUCT_INSTANCE,
+      subscription = EventConstants.DESTINATION
+  )
+  public void onActivateInstance(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                               final String payload) {
+    this.logger.debug("Product instance created.");
+    this.eventRecorder.event(tenant, EventConstants.ACTIVATE_PRODUCT_INSTANCE, payload, String.class);
+  }
+
+  @JmsListener(
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_CLOSE_PRODUCT_INSTANCE,
+      subscription = EventConstants.DESTINATION
+  )
+  public void onCloseInstance(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                               final String payload) {
+    this.logger.debug("Product instance created.");
+    this.eventRecorder.event(tenant, EventConstants.CLOSE_PRODUCT_INSTANCE, payload, String.class);
+  }
 }
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/command/ActivateProductInstanceCommand.java b/service/src/main/java/io/mifos/deposit/service/internal/command/ActivateProductInstanceCommand.java
new file mode 100644
index 0000000..66ad60b
--- /dev/null
+++ b/service/src/main/java/io/mifos/deposit/service/internal/command/ActivateProductInstanceCommand.java
@@ -0,0 +1,29 @@
+/*
+ * 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 ActivateProductInstanceCommand {
+  private final String identifier;
+
+  public ActivateProductInstanceCommand(final String identifier) {
+    super();
+    this.identifier = identifier;
+  }
+
+  public String identifier() {
+    return this.identifier;
+  }
+}
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/command/CloseProductInstanceCommand.java b/service/src/main/java/io/mifos/deposit/service/internal/command/CloseProductInstanceCommand.java
new file mode 100644
index 0000000..8bbb03a
--- /dev/null
+++ b/service/src/main/java/io/mifos/deposit/service/internal/command/CloseProductInstanceCommand.java
@@ -0,0 +1,29 @@
+/*
+ * 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 CloseProductInstanceCommand {
+  private final String identifier;
+
+  public CloseProductInstanceCommand(final String identifier) {
+    super();
+    this.identifier = identifier;
+  }
+
+  public String identifier() {
+    return this.identifier;
+  }
+}
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 4612e0e..124253b 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
@@ -22,6 +22,8 @@
 import io.mifos.deposit.api.v1.EventConstants;
 import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
 import io.mifos.deposit.service.ServiceConstants;
+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.mapper.ProductInstanceMapper;
 import io.mifos.deposit.service.internal.repository.ProductDefinitionEntity;
@@ -29,7 +31,6 @@
 import io.mifos.deposit.service.internal.repository.ProductInstanceEntity;
 import io.mifos.deposit.service.internal.repository.ProductInstanceRepository;
 import io.mifos.deposit.service.internal.service.helper.AccountingService;
-import org.apache.commons.lang.RandomStringUtils;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -82,10 +83,10 @@
 
         final StringBuilder stringBuilder = new StringBuilder();
         final String accountNumber = stringBuilder
-            .append(productDefinitionEntity.getEquityLedgerIdentifier())
-            .append(".")
             .append(productInstance.getCustomerIdentifier())
             .append(".")
+            .append(productDefinitionEntity.getEquityLedgerIdentifier())
+            .append(".")
             .append(String.format("%05d", accountSuffix))
             .toString();
 
@@ -103,4 +104,50 @@
     this.productInstanceRepository.save(productInstanceEntity);
     return productInstance.getCustomerIdentifier();
   }
+
+  @Transactional
+  @CommandHandler
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.ACTIVATE_PRODUCT_INSTANCE)
+  public String process(final ActivateProductInstanceCommand activateProductInstanceCommand) {
+    final String accountIdentifier = activateProductInstanceCommand.identifier();
+    final Optional<ProductInstanceEntity> optionalProductInstance =
+        this.productInstanceRepository.findByAccountIdentifier(accountIdentifier);
+
+    if (optionalProductInstance.isPresent()) {
+      final ProductInstanceEntity productInstanceEntity = optionalProductInstance.get();
+      productInstanceEntity.setState("ACTIVE");
+      productInstanceEntity.setLastModifiedBy(UserContextHolder.checkedGetUser());
+      productInstanceEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC()));
+      this.productInstanceRepository.save(productInstanceEntity);
+
+      return accountIdentifier;
+    } else {
+      this.logger.warn("Product instance for account {} not found.", accountIdentifier);
+    }
+
+    return null;
+  }
+
+  @Transactional
+  @CommandHandler
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.CLOSE_PRODUCT_INSTANCE)
+  public String process(final CloseProductInstanceCommand closeProductInstanceCommand) {
+    final String accountIdentifier = closeProductInstanceCommand.identifier();
+    final Optional<ProductInstanceEntity> optionalProductInstance =
+        this.productInstanceRepository.findByAccountIdentifier(accountIdentifier);
+
+    if (optionalProductInstance.isPresent()) {
+      final ProductInstanceEntity productInstanceEntity = optionalProductInstance.get();
+      productInstanceEntity.setState("CLOSED");
+      productInstanceEntity.setLastModifiedBy(UserContextHolder.checkedGetUser());
+      productInstanceEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC()));
+      this.productInstanceRepository.save(productInstanceEntity);
+
+      return accountIdentifier;
+    } else {
+      this.logger.warn("Product instance for account {} not found.", accountIdentifier);
+    }
+
+    return null;
+  }
 }
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/repository/ProductInstanceRepository.java b/service/src/main/java/io/mifos/deposit/service/internal/repository/ProductInstanceRepository.java
index 930986d..0091204 100644
--- a/service/src/main/java/io/mifos/deposit/service/internal/repository/ProductInstanceRepository.java
+++ b/service/src/main/java/io/mifos/deposit/service/internal/repository/ProductInstanceRepository.java
@@ -18,6 +18,7 @@
 import org.springframework.data.jpa.repository.JpaRepository;
 
 import java.util.List;
+import java.util.Optional;
 
 public interface ProductInstanceRepository extends JpaRepository<ProductInstanceEntity, Long> {
 
@@ -27,4 +28,6 @@
 
   List<ProductInstanceEntity> findByProductDefinitionAndCustomerIdentifier(
       final ProductDefinitionEntity productDefinitionEntity, final String customerIdentifier);
+
+  Optional<ProductInstanceEntity> findByAccountIdentifier(final String identifier);
 }
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/service/ProductInstanceService.java b/service/src/main/java/io/mifos/deposit/service/internal/service/ProductInstanceService.java
index e16698a..53067ed 100644
--- a/service/src/main/java/io/mifos/deposit/service/internal/service/ProductInstanceService.java
+++ b/service/src/main/java/io/mifos/deposit/service/internal/service/ProductInstanceService.java
@@ -65,4 +65,8 @@
           .collect(Collectors.toList())).orElseGet(Collections::emptyList);
 
   }
+
+  public Optional<ProductInstance> findByAccountIdentifier(final String identifier) {
+    return this.productInstanceRepository.findByAccountIdentifier(identifier).map(ProductInstanceMapper::map);
+  }
 }
diff --git a/service/src/main/java/io/mifos/deposit/service/internal/service/helper/AccountingService.java b/service/src/main/java/io/mifos/deposit/service/internal/service/helper/AccountingService.java
index 1d1f1dd..3c68e87 100644
--- a/service/src/main/java/io/mifos/deposit/service/internal/service/helper/AccountingService.java
+++ b/service/src/main/java/io/mifos/deposit/service/internal/service/helper/AccountingService.java
@@ -1,3 +1,18 @@
+/*
+ * 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.service.helper;
 
 import io.mifos.accounting.api.v1.client.LedgerManager;
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 7006b39..59f4a25 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
@@ -18,9 +18,13 @@
 import io.mifos.anubis.annotation.AcceptedTokenType;
 import io.mifos.anubis.annotation.Permittable;
 import io.mifos.core.command.gateway.CommandGateway;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.deposit.api.v1.EventConstants;
 import io.mifos.deposit.api.v1.PermittableGroupIds;
 import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
 import io.mifos.deposit.service.ServiceConstants;
+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.service.ProductInstanceService;
 import org.slf4j.Logger;
@@ -28,6 +32,7 @@
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
@@ -80,4 +85,32 @@
   public ResponseEntity<List<ProductInstance>> fetchProductInstances(@RequestParam(value = "customer", required = true) final String customerIdentifier) {
     return ResponseEntity.ok(this.productInstanceService.findByCustomer(customerIdentifier));
   }
+
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
+  @RequestMapping(
+      value = "/{identifier}",
+      method = RequestMethod.POST,
+      consumes = MediaType.APPLICATION_JSON_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  @ResponseBody
+  ResponseEntity<Void> postProductInstanceCommand(@PathVariable("identifier") final String identifier,
+                                                  @RequestParam(value = "command", required = true) final String command) {
+    if (!this.productInstanceService.findByAccountIdentifier(identifier).isPresent()) {
+      throw ServiceException.notFound("Product instance {0} not found.", identifier);
+    }
+
+    switch (command.toUpperCase()) {
+      case EventConstants.ACTIVATE_PRODUCT_INSTANCE_COMMAND:
+        this.commandGateway.process(new ActivateProductInstanceCommand(identifier));
+        break;
+      case EventConstants.CLOSE_PRODUCT_INSTANCE_COMMAND:
+        this.commandGateway.process(new CloseProductInstanceCommand(identifier));
+        break;
+      default:
+        throw ServiceException.badRequest("Unsupported command {0}", command);
+    }
+
+    return ResponseEntity.accepted().build();
+  }
 }