Implemented marking tasks as executed.
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java b/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
index 7a0550f..9412d57 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
@@ -34,6 +34,7 @@
   String DELETE_TASK_DEFINITION = "delete-task-definition";
   String POST_CHARGE_DEFINITION = "post-charge-definition";
   String PUT_TASK_INSTANCE = "put-task-instance";
+  String PUT_TASK_INSTANCE_EXECUTION = "put-task-instance-execution";
   String PUT_CHARGE_DEFINITION = "put-charge-definition";
   String DELETE_PRODUCT_CHARGE_DEFINITION = "delete-product-charge-definition";
   String SELECTOR_INITIALIZE = SELECTOR_NAME + " = '" + INITIALIZE + "'";
@@ -47,6 +48,7 @@
   String SELECTOR_PUT_TASK_DEFINITION = SELECTOR_NAME + " = '" + PUT_TASK_DEFINITION + "'";
   String SELECTOR_DELETE_TASK_DEFINITION = SELECTOR_NAME + " = '" + DELETE_TASK_DEFINITION + "'";
   String SELECTOR_PUT_TASK_INSTANCE = SELECTOR_NAME + " = '" + PUT_TASK_INSTANCE + "'";
+  String SELECTOR_PUT_TASK_INSTANCE_EXECUTION = SELECTOR_NAME + " = '" + PUT_TASK_INSTANCE_EXECUTION + "'";
   String SELECTOR_POST_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + POST_CHARGE_DEFINITION + "'";
   String SELECTOR_PUT_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + PUT_CHARGE_DEFINITION + "'";
   String SELECTOR_DELETE_PRODUCT_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + DELETE_PRODUCT_CHARGE_DEFINITION + "'";
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestTaskInstances.java b/component-test/src/main/java/io/mifos/portfolio/TestTaskInstances.java
index ce838b5..5f3d324 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestTaskInstances.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestTaskInstances.java
@@ -15,6 +15,8 @@
  */
 package io.mifos.portfolio;
 
+import io.mifos.core.api.util.NotFoundException;
+import io.mifos.core.test.domain.TimeStampChecker;
 import io.mifos.portfolio.api.v1.domain.Case;
 import io.mifos.portfolio.api.v1.domain.Product;
 import io.mifos.portfolio.api.v1.domain.TaskDefinition;
@@ -64,4 +66,43 @@
     taskInstance.setComment("And the words of the prophets are written on the subway walls");
     Assert.assertEquals(taskInstance, taskInstanceChanged);
   }
+
+  @Test
+  public void shouldMarkBasicTaskExecuted() throws InterruptedException {
+    final Product product = createProduct();
+    final TaskDefinition taskDefinition = createTaskDefinition(product);
+
+    enableProduct(product);
+
+    final Case customerCase = createCase(product.getIdentifier());
+
+    final TaskInstance taskInstance = portfolioManager.getTaskForCase(product.getIdentifier(), customerCase.getIdentifier(), taskDefinition.getIdentifier());
+
+    final TimeStampChecker timeStampChecker = TimeStampChecker.roughlyNow();
+    portfolioManager.markTaskExecution(product.getIdentifier(), customerCase.getIdentifier(),  taskDefinition.getIdentifier(), true);
+    Assert.assertTrue(eventRecorder.wait(EventConstants.PUT_TASK_INSTANCE_EXECUTION, new TaskInstanceEvent(product.getIdentifier(), customerCase.getIdentifier(), taskDefinition.getIdentifier())));
+
+    final TaskInstance executedTaskInstance = portfolioManager.getTaskForCase(product.getIdentifier(), customerCase.getIdentifier(), taskDefinition.getIdentifier());
+    timeStampChecker.assertCorrect(executedTaskInstance.getExecutedOn());
+    Assert.assertEquals(TEST_USER, executedTaskInstance.getExecutedBy());
+
+    final List<TaskInstance> tasksForCaseExcludingExecuted = portfolioManager.getAllTasksForCase(product.getIdentifier(), customerCase.getIdentifier(), false);
+    Assert.assertFalse(tasksForCaseExcludingExecuted.contains(taskInstance));
+
+    final List<TaskInstance> allTasksForCase = portfolioManager.getAllTasksForCase(product.getIdentifier(), customerCase.getIdentifier(), true);
+    Assert.assertTrue(allTasksForCase.contains(executedTaskInstance));
+  }
+
+
+  @Test(expected = NotFoundException.class)
+  public void shouldFailToMarkNonExistantTask() throws InterruptedException {
+    final Product product = createProduct();
+    createTaskDefinition(product);
+
+    enableProduct(product);
+
+    final Case customerCase = createCase(product.getIdentifier());
+
+    portfolioManager.markTaskExecution(product.getIdentifier(), customerCase.getIdentifier(),  "blubble", true);
+  }
 }
diff --git a/component-test/src/main/java/io/mifos/portfolio/listener/TaskInstanceEventListener.java b/component-test/src/main/java/io/mifos/portfolio/listener/TaskInstanceEventListener.java
index 6f01215..80baf69 100644
--- a/component-test/src/main/java/io/mifos/portfolio/listener/TaskInstanceEventListener.java
+++ b/component-test/src/main/java/io/mifos/portfolio/listener/TaskInstanceEventListener.java
@@ -45,7 +45,17 @@
       selector = EventConstants.SELECTOR_PUT_TASK_INSTANCE
   )
   public void onChangeTaskInstance(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
-                                     final String payload) {
+                                   final String payload) {
     this.eventRecorder.event(tenant, EventConstants.PUT_TASK_INSTANCE, payload, TaskInstanceEvent.class);
   }
+
+  @JmsListener(
+      subscription = EventConstants.DESTINATION,
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_PUT_TASK_INSTANCE_EXECUTION
+  )
+  public void onChangeTaskInstanceE(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                                   final String payload) {
+    this.eventRecorder.event(tenant, EventConstants.PUT_TASK_INSTANCE_EXECUTION, payload, TaskInstanceEvent.class);
+  }
 }
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/ExecuteTaskInstanceCommand.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/ExecuteTaskInstanceCommand.java
index a1e13f7..e9d740f 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/command/ExecuteTaskInstanceCommand.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/ExecuteTaskInstanceCommand.java
@@ -31,6 +31,22 @@
     this.executed = executed;
   }
 
+  public String getProductIdentifier() {
+    return productIdentifier;
+  }
+
+  public String getCaseIdentifier() {
+    return caseIdentifier;
+  }
+
+  public String getTaskIdentifier() {
+    return taskIdentifier;
+  }
+
+  public Boolean getExecuted() {
+    return executed;
+  }
+
   @Override
   public String toString() {
     return "ExecuteTaskInstanceCommand{" +
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskInstanceCommandHandler.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskInstanceCommandHandler.java
index c7c26fc..3d28ae7 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskInstanceCommandHandler.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskInstanceCommandHandler.java
@@ -15,6 +15,7 @@
  */
 package io.mifos.portfolio.service.internal.command.handler;
 
+import io.mifos.core.api.util.UserContextHolder;
 import io.mifos.core.command.annotation.Aggregate;
 import io.mifos.core.command.annotation.CommandHandler;
 import io.mifos.core.command.annotation.CommandLogLevel;
@@ -24,11 +25,15 @@
 import io.mifos.portfolio.api.v1.events.EventConstants;
 import io.mifos.portfolio.api.v1.events.TaskInstanceEvent;
 import io.mifos.portfolio.service.internal.command.ChangeTaskInstanceCommand;
+import io.mifos.portfolio.service.internal.command.ExecuteTaskInstanceCommand;
 import io.mifos.portfolio.service.internal.mapper.TaskInstanceMapper;
 import io.mifos.portfolio.service.internal.repository.TaskInstanceEntity;
 import io.mifos.portfolio.service.internal.repository.TaskInstanceRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import java.time.Clock;
+import java.time.LocalDateTime;
+
 /**
  * @author Myrle Krantz
  */
@@ -63,4 +68,25 @@
         changeTaskInstanceCommand.getCaseIdentifier(),
         changeTaskInstanceCommand.getInstance().getTaskIdentifier());
   }
+
+  @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PUT_TASK_INSTANCE_EXECUTION)
+  public TaskInstanceEvent process(final ExecuteTaskInstanceCommand changeTaskInstanceExecutionCommand) {
+    final String productIdentifier = changeTaskInstanceExecutionCommand.getProductIdentifier();
+    final String caseIdentifier = changeTaskInstanceExecutionCommand.getCaseIdentifier();
+    final String taskIdentifier = changeTaskInstanceExecutionCommand.getTaskIdentifier();
+    final boolean executed = changeTaskInstanceExecutionCommand.getExecuted();
+
+    final TaskInstanceEntity taskInstanceEntity
+        = taskInstanceRepository.findByProductIdAndCaseIdAndTaskId(productIdentifier, caseIdentifier, taskIdentifier)
+        .orElseThrow(() -> ServiceException.notFound("Task instance ''{0}.{1}.{2}'' not found.",
+            productIdentifier, caseIdentifier, taskIdentifier));
+
+    taskInstanceEntity.setExecutedOn(LocalDateTime.now(Clock.systemUTC()));
+    taskInstanceEntity.setExecutedBy(UserContextHolder.checkedGetUser());
+
+    taskInstanceRepository.save(taskInstanceEntity);
+
+    return new TaskInstanceEvent(productIdentifier, caseIdentifier, taskIdentifier);
+  }
 }