fineract-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From my...@apache.org
Subject [fineract-cn-teller] 09/30: added teller transaction handling
Date Mon, 22 Jan 2018 15:32:07 GMT
This is an automated email from the ASF dual-hosted git repository.

myrle pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract-cn-teller.git

commit 0230a24ec69b004d2785cd9df5cf6637923e7156
Author: mgeiss <mgeiss@mifos.org>
AuthorDate: Wed Jun 14 09:06:42 2017 +0200

    added teller transaction handling
---
 .../io/mifos/teller/api/v1/EventConstants.java     |  13 +
 .../mifos/teller/api/v1/client/TellerManager.java  |   8 +-
 .../java/io/mifos/teller/api/v1/domain/Charge.java |   9 +
 .../teller/api/v1/domain/TellerTransaction.java    |  10 +
 .../java/io/mifos/teller/AbstractTellerTest.java   |   2 +-
 .../java/io/mifos/teller/TestTellerManagement.java |   4 +
 .../java/io/mifos/teller/TestTellerOperation.java  | 292 +++++++++++++++++++++
 .../mifos/teller/listener/TellerEventListener.java |  22 ++
 .../java/io/mifos/teller/ServiceConstants.java     |  11 +
 .../java/io/mifos/teller/TellerConfiguration.java  |   3 +-
 .../command/AuthenticateTellerCommand.java         |  38 +--
 .../CancelTellerTransactionCommand.java}           |  15 +-
 .../ConfirmTellerTransactionCommand.java}          |  15 +-
 .../InitializeTellerTransactionCommand.java        |  38 +--
 .../PauseTellerCommand.java}                       |  15 +-
 .../internal/command/handler/TellerAggregate.java  |  83 +++++-
 .../handler/TellerTransactionAggregate.java        | 124 +++++++++
 .../service/internal/mapper/TellerMapper.java      |   1 -
 .../internal/mapper/TellerTransactionMapper.java   |  59 +++++
 .../processor/TellerTransactionProcessor.java      | 251 ++++++++++++++++++
 .../repository/TellerTransactionEntity.java        |  94 ++++---
 .../repository/TellerTransactionRepository.java    |  32 +++
 .../internal/service/TellerOperationService.java   |  53 ++++
 .../internal/service/helper/AccountingService.java |  15 ++
 .../helper/DepositAccountManagementService.java    |  52 ++++
 .../rest/TellerOperationRestController.java        | 125 +++++++--
 .../db/migrations/mariadb/V1__initial_setup.sql    |  22 +-
 27 files changed, 1280 insertions(+), 126 deletions(-)

diff --git a/api/src/main/java/io/mifos/teller/api/v1/EventConstants.java b/api/src/main/java/io/mifos/teller/api/v1/EventConstants.java
index 34b1abb..fcbd70e 100644
--- a/api/src/main/java/io/mifos/teller/api/v1/EventConstants.java
+++ b/api/src/main/java/io/mifos/teller/api/v1/EventConstants.java
@@ -33,4 +33,17 @@ public interface EventConstants {
   String SELECTOR_OPEN_TELLER = SELECTOR_NAME + " = '" + OPEN_TELLER + "'";
   String CLOSE_TELLER = "close-teller";
   String SELECTOR_CLOSE_TELLER = SELECTOR_NAME + " = '" + CLOSE_TELLER + "'";
+  String ACTIVATE_TELLER = "activate-teller";
+  String SELECTOR_ACTIVATE_TELLER = SELECTOR_NAME + " = '" + ACTIVATE_TELLER + "'";
+  String PAUSE_TELLER = "pause-teller";
+  String SELECTOR_PAUSE_TELLER = SELECTOR_NAME + " = '" + PAUSE_TELLER + "'";
+
+  String INIT_TRANSACTION = "init-transaction";
+  String SELECTOR_INIT_TRANSACTION = SELECTOR_NAME + " = '" + INIT_TRANSACTION + "'";
+  String CONFIRM_TRANSACTION = "confirm-transaction";
+  String SELECTOR_CONFIRM_TRANSACTION = SELECTOR_NAME + " = '" + CONFIRM_TRANSACTION + "'";
+  String CANCEL_TRANSACTION = "cancel-transaction";
+  String SELECTOR_CANCEL_TRANSACTION = SELECTOR_NAME + " = '" + CANCEL_TRANSACTION + "'";
+  String AUTHENTICATE_TELLER = "authenticate-teller";
+  String SELECTOR_AUTHENTICATE_TELLER = SELECTOR_NAME + " = '" + AUTHENTICATE_TELLER + "'";
 }
diff --git a/api/src/main/java/io/mifos/teller/api/v1/client/TellerManager.java b/api/src/main/java/io/mifos/teller/api/v1/client/TellerManager.java
index 0972873..18d5f55 100644
--- a/api/src/main/java/io/mifos/teller/api/v1/client/TellerManager.java
+++ b/api/src/main/java/io/mifos/teller/api/v1/client/TellerManager.java
@@ -125,9 +125,10 @@ public interface TellerManager {
   )
   @ThrowsExceptions({
       @ThrowsException(status = HttpStatus.NOT_FOUND, exception = TellerNotFoundException.class),
-      @ThrowsException(status = HttpStatus.BAD_REQUEST, exception = TellerNotFoundException.class)
+      @ThrowsException(status = HttpStatus.BAD_REQUEST, exception = TellerNotFoundException.class),
+      @ThrowsException(status = HttpStatus.CONFLICT, exception = TellerNotFoundException.class)
   })
-  void auth(@PathVariable("tellerCode") final String tellerCode,
+  String auth(@PathVariable("tellerCode") final String tellerCode,
             @RequestBody @Valid final TellerAuthentication tellerAuthentication);
 
   @RequestMapping(
@@ -137,7 +138,8 @@ public interface TellerManager {
       produces = MediaType.APPLICATION_JSON_VALUE
   )
   @ThrowsExceptions({
-      @ThrowsException(status = HttpStatus.NOT_FOUND, exception = TellerNotFoundException.class)
+      @ThrowsException(status = HttpStatus.NOT_FOUND, exception = TellerNotFoundException.class),
+      @ThrowsException(status = HttpStatus.BAD_REQUEST, exception = TellerValidationException.class)
   })
   void post(@PathVariable("tellerCode") final String tellerCode,
             @RequestParam(value = "command", required = true) final String command);
diff --git a/api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java b/api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java
index e576a09..4e375fe 100644
--- a/api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java
+++ b/api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java
@@ -18,6 +18,7 @@ package io.mifos.teller.api.v1.domain;
 public class Charge {
 
   private String code;
+  private String incomeAccountIdentifier;
   private String name;
   private Double amount;
 
@@ -33,6 +34,14 @@ public class Charge {
     this.code = code;
   }
 
+  public String getIncomeAccountIdentifier() {
+    return this.incomeAccountIdentifier;
+  }
+
+  public void setIncomeAccountIdentifier(final String incomeAccountIdentifier) {
+    this.incomeAccountIdentifier = incomeAccountIdentifier;
+  }
+
   public String getName() {
     return this.name;
   }
diff --git a/api/src/main/java/io/mifos/teller/api/v1/domain/TellerTransaction.java b/api/src/main/java/io/mifos/teller/api/v1/domain/TellerTransaction.java
index 5dc8ffa..926e48b 100644
--- a/api/src/main/java/io/mifos/teller/api/v1/domain/TellerTransaction.java
+++ b/api/src/main/java/io/mifos/teller/api/v1/domain/TellerTransaction.java
@@ -35,6 +35,8 @@ public class TellerTransaction {
   @NotNull
   private String transactionDate;
   @ValidIdentifier
+  private String customerIdentifier;
+  @ValidIdentifier
   private String productIdentifier;
   @ValidIdentifier(optional = true)
   private String productCaseIdentifier;
@@ -77,6 +79,14 @@ public class TellerTransaction {
     this.transactionDate = transactionDate;
   }
 
+  public String getCustomerIdentifier() {
+    return this.customerIdentifier;
+  }
+
+  public void setCustomerIdentifier(final String customerIdentifier) {
+    this.customerIdentifier = customerIdentifier;
+  }
+
   public String getProductIdentifier() {
     return this.productIdentifier;
   }
diff --git a/component-test/src/main/java/io/mifos/teller/AbstractTellerTest.java b/component-test/src/main/java/io/mifos/teller/AbstractTellerTest.java
index 4d60f41..9a86a73 100644
--- a/component-test/src/main/java/io/mifos/teller/AbstractTellerTest.java
+++ b/component-test/src/main/java/io/mifos/teller/AbstractTellerTest.java
@@ -80,7 +80,7 @@ public class AbstractTellerTest {
     }
   }
 
-  private static final String TEST_USER = "homer";
+  static final String TEST_USER = "homer";
 
   private final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
   private final static CassandraInitializer cassandraInitializer = new CassandraInitializer();
diff --git a/component-test/src/main/java/io/mifos/teller/TestTellerManagement.java b/component-test/src/main/java/io/mifos/teller/TestTellerManagement.java
index b9f703d..ca28069 100644
--- a/component-test/src/main/java/io/mifos/teller/TestTellerManagement.java
+++ b/component-test/src/main/java/io/mifos/teller/TestTellerManagement.java
@@ -361,6 +361,7 @@ public class TestTellerManagement extends AbstractTellerTest {
 
     final TellerManagementCommand command = new TellerManagementCommand();
     command.setAction(TellerManagementCommand.Action.OPEN.name());
+    command.setAdjustment(TellerManagementCommand.Adjustment.NONE.name());
     command.setAssignedEmployeeIdentifier(RandomStringUtils.randomAlphanumeric(32));
 
     Mockito.doAnswer(invocation -> true)
@@ -395,6 +396,7 @@ public class TestTellerManagement extends AbstractTellerTest {
 
     final TellerManagementCommand command = new TellerManagementCommand();
     command.setAction(TellerManagementCommand.Action.OPEN.name());
+    command.setAdjustment(TellerManagementCommand.Adjustment.NONE.name());
     command.setAssignedEmployeeIdentifier(RandomStringUtils.randomAlphanumeric(32));
 
     Mockito.doAnswer(invocation -> true)
@@ -435,6 +437,7 @@ public class TestTellerManagement extends AbstractTellerTest {
 
     final TellerManagementCommand command = new TellerManagementCommand();
     command.setAction(TellerManagementCommand.Action.CLOSE.name());
+    command.setAdjustment(TellerManagementCommand.Adjustment.NONE.name());
 
     super.testSubject.post(officeIdentifier, teller.getCode(), command);
   }
@@ -459,6 +462,7 @@ public class TestTellerManagement extends AbstractTellerTest {
 
     final TellerManagementCommand command = new TellerManagementCommand();
     command.setAction(TellerManagementCommand.Action.OPEN.name());
+    command.setAdjustment(TellerManagementCommand.Adjustment.NONE.name());
     command.setAssignedEmployeeIdentifier(RandomStringUtils.randomAlphanumeric(32));
 
     Mockito.doAnswer(invocation -> true)
diff --git a/component-test/src/main/java/io/mifos/teller/TestTellerOperation.java b/component-test/src/main/java/io/mifos/teller/TestTellerOperation.java
new file mode 100644
index 0000000..39b7ed3
--- /dev/null
+++ b/component-test/src/main/java/io/mifos/teller/TestTellerOperation.java
@@ -0,0 +1,292 @@
+/*
+ * 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.teller;
+
+import io.mifos.core.lang.DateConverter;
+import io.mifos.teller.api.v1.EventConstants;
+import io.mifos.teller.api.v1.client.TellerNotFoundException;
+import io.mifos.teller.api.v1.client.TellerValidationException;
+import io.mifos.teller.api.v1.domain.Teller;
+import io.mifos.teller.api.v1.domain.TellerAuthentication;
+import io.mifos.teller.api.v1.domain.TellerManagementCommand;
+import io.mifos.teller.api.v1.domain.TellerTransaction;
+import io.mifos.teller.util.TellerGenerator;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.Collections;
+
+public class TestTellerOperation extends AbstractTellerTest {
+
+  public TestTellerOperation() {
+    super();
+  }
+
+  @Test
+  public void shouldAuthenticate() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    tellerAuthentication.setPassword(teller.getPassword().getBytes());
+
+    final String auth = super.testSubject.auth(teller.getCode(), tellerAuthentication);
+
+    Assert.assertEquals(auth, teller.getCode());
+  }
+
+
+  @Test(expected = TellerNotFoundException.class)
+  public void shouldNotAuthenticateUserMismatch() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier("unassigneduser");
+    tellerAuthentication.setPassword(teller.getPassword().getBytes());
+
+    super.testSubject.auth(teller.getCode(), tellerAuthentication);
+  }
+
+  @Test(expected = TellerNotFoundException.class)
+  public void shouldNotAuthenticatePasswordMismatch() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    tellerAuthentication.setPassword("wrongpasword".getBytes());
+
+    super.testSubject.auth(teller.getCode(), tellerAuthentication);
+  }
+
+  @Test
+  public void shouldPauseTeller() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    tellerAuthentication.setPassword(teller.getPassword().getBytes());
+
+    super.testSubject.auth(teller.getCode(), tellerAuthentication);
+
+    super.eventRecorder.wait(EventConstants.AUTHENTICATE_TELLER, teller.getCode());
+
+    super.testSubject.post(teller.getCode(), "PAUSE");
+
+    Assert.assertTrue(super.eventRecorder.wait(EventConstants.PAUSE_TELLER, teller.getCode()));
+  }
+
+  @Test(expected = TellerValidationException.class)
+  public void shouldNotPauseTellerNotAuthenticated() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    super.testSubject.post(teller.getCode(), "PAUSE");
+  }
+
+  @Test
+  public void shouldOpenAccount() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    tellerAuthentication.setPassword(teller.getPassword().getBytes());
+
+    super.testSubject.auth(teller.getCode(), tellerAuthentication);
+
+    super.eventRecorder.wait(EventConstants.AUTHENTICATE_TELLER, teller.getCode());
+
+    final TellerTransaction tellerTransaction =  new TellerTransaction();
+    tellerTransaction.setTransactionType(ServiceConstants.TX_OPEN_ACCOUNT);
+    tellerTransaction.setTransactionDate(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    tellerTransaction.setProductIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerAccountIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setClerk(AbstractTellerTest.TEST_USER);
+    tellerTransaction.setAmount(1234.56D);
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.accountingServiceSpy).accountExists(tellerTransaction.getCustomerAccountIdentifier());
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getCharges(Matchers.eq(tellerTransaction));
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getProductInstance(tellerTransaction.getCustomerIdentifier());
+
+    super.testSubject.post(teller.getCode(), tellerTransaction);
+  }
+
+  @Test
+  public void shouldCloseAccount() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    tellerAuthentication.setPassword(teller.getPassword().getBytes());
+
+    super.testSubject.auth(teller.getCode(), tellerAuthentication);
+
+    super.eventRecorder.wait(EventConstants.AUTHENTICATE_TELLER, teller.getCode());
+
+    final TellerTransaction tellerTransaction =  new TellerTransaction();
+    tellerTransaction.setTransactionType(ServiceConstants.TX_CLOSE_ACCOUNT);
+    tellerTransaction.setTransactionDate(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    tellerTransaction.setProductIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerAccountIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setClerk(AbstractTellerTest.TEST_USER);
+    tellerTransaction.setAmount(1234.56D);
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.accountingServiceSpy).accountExists(tellerTransaction.getCustomerAccountIdentifier());
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getCharges(Matchers.eq(tellerTransaction));
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getProductInstance(tellerTransaction.getCustomerIdentifier());
+
+    super.testSubject.post(teller.getCode(), tellerTransaction);
+  }
+
+  @Test
+  public void shouldTransferAccountToAccount() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    tellerAuthentication.setPassword(teller.getPassword().getBytes());
+
+    super.testSubject.auth(teller.getCode(), tellerAuthentication);
+
+    super.eventRecorder.wait(EventConstants.AUTHENTICATE_TELLER, teller.getCode());
+
+    final TellerTransaction tellerTransaction =  new TellerTransaction();
+    tellerTransaction.setTransactionType(ServiceConstants.TX_ACCOUNT_TRANSFER);
+    tellerTransaction.setTransactionDate(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    tellerTransaction.setProductIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerAccountIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setTargetAccountIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setClerk(AbstractTellerTest.TEST_USER);
+    tellerTransaction.setAmount(1234.56D);
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.accountingServiceSpy).accountExists(tellerTransaction.getCustomerAccountIdentifier());
+    Mockito.doAnswer(invocation -> true)
+        .when(super.accountingServiceSpy).accountExists(tellerTransaction.getTargetAccountIdentifier());
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getCharges(Matchers.eq(tellerTransaction));
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getProductInstance(tellerTransaction.getCustomerIdentifier());
+
+    super.testSubject.post(teller.getCode(), tellerTransaction);
+  }
+
+  @Test
+  public void shouldDeposit() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    tellerAuthentication.setPassword(teller.getPassword().getBytes());
+
+    super.testSubject.auth(teller.getCode(), tellerAuthentication);
+
+    super.eventRecorder.wait(EventConstants.AUTHENTICATE_TELLER, teller.getCode());
+
+    final TellerTransaction tellerTransaction =  new TellerTransaction();
+    tellerTransaction.setTransactionType(ServiceConstants.TX_CASH_DEPOSIT);
+    tellerTransaction.setTransactionDate(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    tellerTransaction.setProductIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerAccountIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setClerk(AbstractTellerTest.TEST_USER);
+    tellerTransaction.setAmount(1234.56D);
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.accountingServiceSpy).accountExists(tellerTransaction.getCustomerAccountIdentifier());
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getCharges(Matchers.eq(tellerTransaction));
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getProductInstance(tellerTransaction.getCustomerIdentifier());
+
+    super.testSubject.post(teller.getCode(), tellerTransaction);
+  }
+
+  @Test
+  public void shouldWithdraw() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final TellerAuthentication tellerAuthentication = new TellerAuthentication();
+    tellerAuthentication.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    tellerAuthentication.setPassword(teller.getPassword().getBytes());
+
+    super.testSubject.auth(teller.getCode(), tellerAuthentication);
+
+    super.eventRecorder.wait(EventConstants.AUTHENTICATE_TELLER, teller.getCode());
+
+    final TellerTransaction tellerTransaction =  new TellerTransaction();
+    tellerTransaction.setTransactionType(ServiceConstants.TX_CASH_WITHDRAWAL);
+    tellerTransaction.setTransactionDate(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    tellerTransaction.setProductIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerAccountIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setCustomerIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    tellerTransaction.setClerk(AbstractTellerTest.TEST_USER);
+    tellerTransaction.setAmount(1234.56D);
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.accountingServiceSpy).accountExists(tellerTransaction.getCustomerAccountIdentifier());
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getCharges(Matchers.eq(tellerTransaction));
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getProductInstance(tellerTransaction.getCustomerIdentifier());
+
+    super.testSubject.post(teller.getCode(), tellerTransaction);
+  }
+
+  private Teller prepareTeller() throws Exception {
+    final String officeIdentifier = RandomStringUtils.randomAlphabetic(32);
+    final Teller teller = TellerGenerator.createRandomTeller();
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.organizationServiceSpy).officeExists(Matchers.eq(officeIdentifier));
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.accountingServiceSpy).accountExists(Matchers.eq(teller.getTellerAccountIdentifier()));
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.accountingServiceSpy).accountExists(Matchers.eq(teller.getVaultAccountIdentifier()));
+
+    super.testSubject.create(officeIdentifier, teller);
+
+    Assert.assertTrue(super.eventRecorder.wait(EventConstants.POST_TELLER, teller.getCode()));
+
+    final TellerManagementCommand command = new TellerManagementCommand();
+    command.setAction(TellerManagementCommand.Action.OPEN.name());
+    command.setAdjustment(TellerManagementCommand.Adjustment.NONE.name());
+    command.setAssignedEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+
+    Mockito.doAnswer(invocation -> true)
+        .when(super.organizationServiceSpy).employeeExists(Matchers.eq(command.getAssignedEmployeeIdentifier()));
+
+    super.testSubject.post(officeIdentifier, teller.getCode(), command);
+
+    Assert.assertTrue(super.eventRecorder.wait(EventConstants.OPEN_TELLER, teller.getCode()));
+
+    return teller;
+  }
+}
diff --git a/component-test/src/main/java/io/mifos/teller/listener/TellerEventListener.java b/component-test/src/main/java/io/mifos/teller/listener/TellerEventListener.java
index ee40f75..0ad678a 100644
--- a/component-test/src/main/java/io/mifos/teller/listener/TellerEventListener.java
+++ b/component-test/src/main/java/io/mifos/teller/listener/TellerEventListener.java
@@ -81,4 +81,26 @@ public class TellerEventListener {
     this.logger.debug("Teller {} created.", payload);
     this.eventRecorder.event(tenant, EventConstants.CLOSE_TELLER, payload, String.class);
   }
+
+  @JmsListener(
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_AUTHENTICATE_TELLER,
+      subscription = EventConstants.DESTINATION
+  )
+  public void onAuthenticate(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                      final String payload) {
+    this.logger.debug("Teller {} created.", payload);
+    this.eventRecorder.event(tenant, EventConstants.AUTHENTICATE_TELLER, payload, String.class);
+  }
+
+  @JmsListener(
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_PAUSE_TELLER,
+      subscription = EventConstants.DESTINATION
+  )
+  public void onPause(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                             final String payload) {
+    this.logger.debug("Teller {} created.", payload);
+    this.eventRecorder.event(tenant, EventConstants.PAUSE_TELLER, payload, String.class);
+  }
 }
diff --git a/service/src/main/java/io/mifos/teller/ServiceConstants.java b/service/src/main/java/io/mifos/teller/ServiceConstants.java
index adfef4e..2d5f000 100644
--- a/service/src/main/java/io/mifos/teller/ServiceConstants.java
+++ b/service/src/main/java/io/mifos/teller/ServiceConstants.java
@@ -17,6 +17,17 @@ package io.mifos.teller;
 
 public interface ServiceConstants {
   String LOGGER_NAME = "teller-logger";
+
   int ITERATION_COUNT = 2048;
   int LENGTH = 2048;
+
+  String TX_OPEN_ACCOUNT = "ACCO";
+  String TX_CLOSE_ACCOUNT = "ACCC";
+  String TX_ACCOUNT_TRANSFER = "BACT";
+  String TX_CASH_DEPOSIT = "BCDP";
+  String TX_CASH_WITHDRAWAL = "BCWD";
+
+  String TX_DEPOSIT_ADJUSTMENT = "DAJT";
+  String TX_CREDIT_ADJUSTMENT = "CAJT";
+  String TX_CHARGES = "CHRG";
 }
diff --git a/service/src/main/java/io/mifos/teller/TellerConfiguration.java b/service/src/main/java/io/mifos/teller/TellerConfiguration.java
index 5c60d5b..e6c70f6 100644
--- a/service/src/main/java/io/mifos/teller/TellerConfiguration.java
+++ b/service/src/main/java/io/mifos/teller/TellerConfiguration.java
@@ -63,7 +63,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
     "io.mifos.teller.service.rest",
     "io.mifos.teller.service.internal.service",
     "io.mifos.teller.service.internal.repository",
-    "io.mifos.teller.service.internal.command.handler"
+    "io.mifos.teller.service.internal.command.handler",
+    "io.mifos.teller.service.internal.processor"
 })
 @EnableJpaRepositories({
     "io.mifos.teller.service.internal.repository"
diff --git a/api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java b/service/src/main/java/io/mifos/teller/service/internal/command/AuthenticateTellerCommand.java
similarity index 51%
copy from api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java
copy to service/src/main/java/io/mifos/teller/service/internal/command/AuthenticateTellerCommand.java
index e576a09..f1f5ce3 100644
--- a/api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/command/AuthenticateTellerCommand.java
@@ -13,39 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.teller.api.v1.domain;
+package io.mifos.teller.service.internal.command;
 
-public class Charge {
+import io.mifos.teller.api.v1.domain.TellerAuthentication;
 
-  private String code;
-  private String name;
-  private Double amount;
+public class AuthenticateTellerCommand {
+  private final String tellerCode;
+  private final TellerAuthentication tellerAuthentication;
 
-  public Charge() {
+  public AuthenticateTellerCommand(final String tellerCode, final TellerAuthentication tellerAuthentication) {
     super();
+    this.tellerCode = tellerCode;
+    this.tellerAuthentication = tellerAuthentication;
   }
 
-  public String getCode() {
-    return this.code;
+  public String tellerCode() {
+    return this.tellerCode;
   }
 
-  public void setCode(final String code) {
-    this.code = code;
-  }
-
-  public String getName() {
-    return this.name;
-  }
-
-  public void setName(final String name) {
-    this.name = name;
-  }
-
-  public Double getAmount() {
-    return this.amount;
-  }
-
-  public void setAmount(final Double amount) {
-    this.amount = amount;
+  public TellerAuthentication tellerAuthentication() {
+    return this.tellerAuthentication;
   }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java b/service/src/main/java/io/mifos/teller/service/internal/command/CancelTellerTransactionCommand.java
similarity index 59%
copy from service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
copy to service/src/main/java/io/mifos/teller/service/internal/command/CancelTellerTransactionCommand.java
index 220abb6..91508e2 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/command/CancelTellerTransactionCommand.java
@@ -13,10 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.teller.service.internal.service;
+package io.mifos.teller.service.internal.command;
 
-import org.springframework.stereotype.Service;
+public class CancelTellerTransactionCommand {
+  private final String tellerTransactionIdentifier;
 
-@Service
-public class TellerOperationService {
+  public CancelTellerTransactionCommand(final String tellerTransactionIdentifier) {
+    super();
+    this.tellerTransactionIdentifier = tellerTransactionIdentifier;
+  }
+
+  public String tellerTransactionIdentifier() {
+    return this.tellerTransactionIdentifier;
+  }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java b/service/src/main/java/io/mifos/teller/service/internal/command/ConfirmTellerTransactionCommand.java
similarity index 59%
copy from service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
copy to service/src/main/java/io/mifos/teller/service/internal/command/ConfirmTellerTransactionCommand.java
index 220abb6..1da91da 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/command/ConfirmTellerTransactionCommand.java
@@ -13,10 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.teller.service.internal.service;
+package io.mifos.teller.service.internal.command;
 
-import org.springframework.stereotype.Service;
+public class ConfirmTellerTransactionCommand {
+  private final String tellerTransactionIdentifier;
 
-@Service
-public class TellerOperationService {
+  public ConfirmTellerTransactionCommand(final String tellerTransactionIdentifier) {
+    super();
+    this.tellerTransactionIdentifier = tellerTransactionIdentifier;
+  }
+
+  public String tellerTransactionIdentifier() {
+    return this.tellerTransactionIdentifier;
+  }
 }
diff --git a/api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java b/service/src/main/java/io/mifos/teller/service/internal/command/InitializeTellerTransactionCommand.java
similarity index 52%
copy from api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java
copy to service/src/main/java/io/mifos/teller/service/internal/command/InitializeTellerTransactionCommand.java
index e576a09..a8040ed 100644
--- a/api/src/main/java/io/mifos/teller/api/v1/domain/Charge.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/command/InitializeTellerTransactionCommand.java
@@ -13,39 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.teller.api.v1.domain;
+package io.mifos.teller.service.internal.command;
 
-public class Charge {
+import io.mifos.teller.api.v1.domain.TellerTransaction;
 
-  private String code;
-  private String name;
-  private Double amount;
+public class InitializeTellerTransactionCommand {
+  private final String tellerCode;
+  private final TellerTransaction tellerTransaction;
 
-  public Charge() {
+  public InitializeTellerTransactionCommand(final String tellerCode, final TellerTransaction tellerTransaction) {
     super();
+    this.tellerCode = tellerCode;
+    this.tellerTransaction = tellerTransaction;
   }
 
-  public String getCode() {
-    return this.code;
+  public String tellerCode() {
+    return this.tellerCode;
   }
 
-  public void setCode(final String code) {
-    this.code = code;
-  }
-
-  public String getName() {
-    return this.name;
-  }
-
-  public void setName(final String name) {
-    this.name = name;
-  }
-
-  public Double getAmount() {
-    return this.amount;
-  }
-
-  public void setAmount(final Double amount) {
-    this.amount = amount;
+  public TellerTransaction tellerTransaction() {
+    return this.tellerTransaction;
   }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java b/service/src/main/java/io/mifos/teller/service/internal/command/PauseTellerCommand.java
similarity index 67%
copy from service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
copy to service/src/main/java/io/mifos/teller/service/internal/command/PauseTellerCommand.java
index 220abb6..a218c90 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/command/PauseTellerCommand.java
@@ -13,10 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.teller.service.internal.service;
+package io.mifos.teller.service.internal.command;
 
-import org.springframework.stereotype.Service;
+public class PauseTellerCommand {
+  private final String tellerCode;
 
-@Service
-public class TellerOperationService {
+  public PauseTellerCommand(final String tellerCode) {
+    super();
+    this.tellerCode = tellerCode;
+  }
+
+  public String tellerCode() {
+    return this.tellerCode;
+  }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/internal/command/handler/TellerAggregate.java b/service/src/main/java/io/mifos/teller/service/internal/command/handler/TellerAggregate.java
index 9fb534f..60abcb2 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/command/handler/TellerAggregate.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/command/handler/TellerAggregate.java
@@ -24,14 +24,18 @@ import io.mifos.core.command.annotation.Aggregate;
 import io.mifos.core.command.annotation.CommandHandler;
 import io.mifos.core.command.annotation.EventEmitter;
 import io.mifos.core.lang.DateConverter;
+import io.mifos.core.lang.ServiceException;
 import io.mifos.teller.ServiceConstants;
 import io.mifos.teller.api.v1.EventConstants;
 import io.mifos.teller.api.v1.domain.Teller;
+import io.mifos.teller.api.v1.domain.TellerAuthentication;
 import io.mifos.teller.api.v1.domain.TellerManagementCommand;
+import io.mifos.teller.service.internal.command.AuthenticateTellerCommand;
 import io.mifos.teller.service.internal.command.ChangeTellerCommand;
 import io.mifos.teller.service.internal.command.CloseTellerCommand;
 import io.mifos.teller.service.internal.command.CreateTellerCommand;
 import io.mifos.teller.service.internal.command.OpenTellerCommand;
+import io.mifos.teller.service.internal.command.PauseTellerCommand;
 import io.mifos.teller.service.internal.mapper.TellerMapper;
 import io.mifos.teller.service.internal.repository.TellerEntity;
 import io.mifos.teller.service.internal.repository.TellerRepository;
@@ -113,7 +117,9 @@ public class TellerAggregate {
       if (optionalTellerEntity.isPresent()) {
         final TellerEntity tellerEntity = optionalTellerEntity.get();
 
-        this.encryptPassword(teller, tellerEntity);
+        if (teller.getPassword() != null) {
+          this.encryptPassword(teller, tellerEntity);
+        }
 
         tellerEntity.setTellerAccountIdentifier(teller.getTellerAccountIdentifier());
         tellerEntity.setVaultAccountIdentifier(teller.getVaultAccountIdentifier());
@@ -145,7 +151,9 @@ public class TellerAggregate {
 
       if (this.checkPreconditions(tellerEntity.getOfficeIdentifier(), TellerMapper.map(tellerEntity))) {
 
-        if (tellerManagementCommand.getAmount() != null && tellerManagementCommand.getAmount() > 0.00D) {
+        if (!tellerManagementCommand.getAdjustment().equals(TellerManagementCommand.Adjustment.NONE.name())
+            && tellerManagementCommand.getAmount() != null
+            && tellerManagementCommand.getAmount() > 0.00D) {
           this.accountingService.postJournalEntry(this.createJournalEntry(tellerEntity, tellerManagementCommand));
         }
 
@@ -177,7 +185,9 @@ public class TellerAggregate {
 
       if (this.checkPreconditions(tellerEntity.getOfficeIdentifier(), TellerMapper.map(tellerEntity))) {
 
-        if (tellerManagementCommand.getAmount() != null && tellerManagementCommand.getAmount() > 0.00D) {
+        if (!tellerManagementCommand.getAdjustment().equals(TellerManagementCommand.Adjustment.NONE.name())
+            && tellerManagementCommand.getAmount() != null
+            && tellerManagementCommand.getAmount() > 0.00D) {
           this.accountingService.postJournalEntry(this.createJournalEntry(tellerEntity, tellerManagementCommand));
         }
         tellerEntity.setAssignedEmployeeIdentifier(null);
@@ -195,6 +205,69 @@ public class TellerAggregate {
     }
   }
 
+  @Transactional
+  @CommandHandler
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.AUTHENTICATE_TELLER)
+  public String process(final AuthenticateTellerCommand authenticateTellerCommand) {
+    final String tellerCode = authenticateTellerCommand.tellerCode();
+    final TellerAuthentication tellerAuthentication = authenticateTellerCommand.tellerAuthentication();
+
+    final Optional<TellerEntity> optionalTeller = this.tellerRepository.findByIdentifier(tellerCode);
+    if (optionalTeller.isPresent()) {
+      final TellerEntity tellerEntity = optionalTeller.get();
+      if (!tellerEntity.getState().equals(Teller.State.OPEN.name())
+          && !tellerEntity.getState().equals(Teller.State.PAUSED.name())) {
+        throw ServiceException.notFound("Teller {0} not found.", tellerCode);
+      }
+
+      if (!UserContextHolder.checkedGetUser().equals(tellerEntity.getAssignedEmployeeIdentifier())) {
+        throw ServiceException.notFound("Teller {0} not found.", tellerCode);
+      }
+
+      final byte[] givenPassword = this.hashGenerator.hash(new String(tellerAuthentication.getPassword()),
+          Base64Utils.decodeFromString(tellerEntity.getSalt()), ServiceConstants.ITERATION_COUNT, ServiceConstants.LENGTH);
+
+      if (!tellerEntity.getPassword().equals(Base64Utils.encodeToString(givenPassword))) {
+        throw ServiceException.notFound("Teller {0} not found.", tellerCode);
+      }
+
+      tellerEntity.setState(Teller.State.ACTIVE.name());
+      tellerEntity.setLastModifiedBy(UserContextHolder.checkedGetUser());
+      tellerEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC()));
+      this.tellerRepository.save(tellerEntity);
+
+      return tellerCode;
+    } else {
+      throw ServiceException.notFound("Teller {0} not found.", tellerCode);
+    }
+  }
+
+  @Transactional
+  @CommandHandler
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PAUSE_TELLER)
+  public String process(final PauseTellerCommand pauseTellerCommand) {
+    final String tellerCode = pauseTellerCommand.tellerCode();
+
+    final Optional<TellerEntity> optionalTeller = this.tellerRepository.findByIdentifier(tellerCode);
+    if (optionalTeller.isPresent()) {
+      final TellerEntity tellerEntity = optionalTeller.get();
+      if (UserContextHolder.checkedGetUser().equals(tellerEntity.getAssignedEmployeeIdentifier())
+          && tellerEntity.getState().equals(Teller.State.ACTIVE.name())) {
+        tellerEntity.setState(Teller.State.PAUSED.name());
+        tellerEntity.setLastModifiedBy(UserContextHolder.checkedGetUser());
+        tellerEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC()));
+        this.tellerRepository.save(tellerEntity);
+
+        return tellerCode;
+      } else {
+        this.logger.warn("Unable to pause teller {}.", tellerCode);
+      }
+    } else {
+      this.logger.warn("Teller {} not found.", tellerCode);
+    }
+    return null;
+  }
+
   private boolean checkPreconditions(final String officeIdentifier, final Teller teller) {
     boolean pass = true;
 
@@ -245,7 +318,7 @@ public class TellerAggregate {
     final Creditor creditor = new Creditor();
     switch (adjustment) {
       case DEBIT:
-        journalEntry.setTransactionType("DAJT");
+        journalEntry.setTransactionType(ServiceConstants.TX_DEPOSIT_ADJUSTMENT);
 
         debtor.setAccountNumber(tellerEntity.getTellerAccountIdentifier());
         debtor.setAmount(tellerManagementCommand.getAmount().toString());
@@ -257,7 +330,7 @@ public class TellerAggregate {
 
         break;
       case CREDIT:
-        journalEntry.setTransactionType("CAJT");
+        journalEntry.setTransactionType(ServiceConstants.TX_CREDIT_ADJUSTMENT);
 
         debtor.setAccountNumber(tellerEntity.getVaultAccountIdentifier());
         debtor.setAmount(tellerManagementCommand.getAmount().toString());
diff --git a/service/src/main/java/io/mifos/teller/service/internal/command/handler/TellerTransactionAggregate.java b/service/src/main/java/io/mifos/teller/service/internal/command/handler/TellerTransactionAggregate.java
new file mode 100644
index 0000000..5d59db4
--- /dev/null
+++ b/service/src/main/java/io/mifos/teller/service/internal/command/handler/TellerTransactionAggregate.java
@@ -0,0 +1,124 @@
+/*
+ * 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.teller.service.internal.command.handler;
+
+import io.mifos.core.command.annotation.Aggregate;
+import io.mifos.core.command.annotation.CommandHandler;
+import io.mifos.core.command.annotation.EventEmitter;
+import io.mifos.teller.ServiceConstants;
+import io.mifos.teller.api.v1.EventConstants;
+import io.mifos.teller.api.v1.domain.TellerTransaction;
+import io.mifos.teller.api.v1.domain.TellerTransactionCosts;
+import io.mifos.teller.service.internal.command.CancelTellerTransactionCommand;
+import io.mifos.teller.service.internal.command.ConfirmTellerTransactionCommand;
+import io.mifos.teller.service.internal.command.InitializeTellerTransactionCommand;
+import io.mifos.teller.service.internal.mapper.TellerTransactionMapper;
+import io.mifos.teller.service.internal.processor.TellerTransactionProcessor;
+import io.mifos.teller.service.internal.repository.TellerEntity;
+import io.mifos.teller.service.internal.repository.TellerRepository;
+import io.mifos.teller.service.internal.repository.TellerTransactionEntity;
+import io.mifos.teller.service.internal.repository.TellerTransactionRepository;
+import org.apache.commons.lang.RandomStringUtils;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Optional;
+
+@Aggregate
+public class TellerTransactionAggregate {
+
+  private final Logger logger;
+  private final TellerTransactionRepository tellerTransactionRepository;
+  private final TellerTransactionProcessor tellerTransactionProcessor;
+  private final TellerRepository tellerRepository;
+
+  @Autowired
+  public TellerTransactionAggregate(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                                    final TellerTransactionRepository tellerTransactionRepository,
+                                    final TellerTransactionProcessor tellerTransactionProcessor,
+                                    final TellerRepository tellerRepository) {
+    super();
+    this.logger = logger;
+    this.tellerTransactionRepository = tellerTransactionRepository;
+    this.tellerTransactionProcessor = tellerTransactionProcessor;
+    this.tellerRepository = tellerRepository;
+  }
+
+  @Transactional
+  @CommandHandler
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.INIT_TRANSACTION)
+  public TellerTransactionCosts process(final InitializeTellerTransactionCommand initializeTellerTransactionCommand) {
+    final String tellerCode = initializeTellerTransactionCommand.tellerCode();
+    final TellerTransaction tellerTransaction = initializeTellerTransactionCommand.tellerTransaction();
+
+    final Optional<TellerEntity> optionalTeller = this.tellerRepository.findByIdentifier(tellerCode);
+    if (optionalTeller.isPresent()) {
+      tellerTransaction.setIdentifier(RandomStringUtils.randomAlphanumeric(32));
+      tellerTransaction.setState(TellerTransaction.State.PENDING.name());
+      final TellerTransactionEntity tellerTransactionEntity = TellerTransactionMapper.map(tellerTransaction);
+      tellerTransactionEntity.setTeller(optionalTeller.get());
+      this.tellerTransactionRepository.save(tellerTransactionEntity);
+      return this.tellerTransactionProcessor.getCosts(tellerTransaction);
+    } else {
+      this.logger.warn("Teller {} not found.", tellerCode);
+    }
+    return null;
+  }
+
+  @Transactional
+  @CommandHandler
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.CONFIRM_TRANSACTION)
+  public String process(final ConfirmTellerTransactionCommand confirmTellerTransactionCommand) {
+    final Optional<TellerTransactionEntity> optionalTellerTransaction =
+        this.tellerTransactionRepository.findByIdentifier(confirmTellerTransactionCommand.tellerTransactionIdentifier());
+
+    if (optionalTellerTransaction.isPresent()) {
+      final TellerTransactionEntity tellerTransactionEntity = optionalTellerTransaction.get();
+
+      this.tellerTransactionProcessor.process(tellerTransactionEntity.getTeller().getIdentifier(),
+          TellerTransactionMapper.map(tellerTransactionEntity));
+
+      tellerTransactionEntity.setState(TellerTransaction.State.CONFIRMED.name());
+      this.tellerTransactionRepository.save(tellerTransactionEntity);
+
+      return confirmTellerTransactionCommand.tellerTransactionIdentifier();
+    } else {
+      this.logger.warn("Teller transaction {} not found", confirmTellerTransactionCommand.tellerTransactionIdentifier());
+    }
+    return null;
+  }
+
+  @Transactional
+  @CommandHandler
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.CANCEL_TRANSACTION)
+  public String process(final CancelTellerTransactionCommand cancelTellerTransactionCommand) {
+    final Optional<TellerTransactionEntity> optionalTellerTransaction =
+        this.tellerTransactionRepository.findByIdentifier(cancelTellerTransactionCommand.tellerTransactionIdentifier());
+
+    if (optionalTellerTransaction.isPresent()) {
+      final TellerTransactionEntity tellerTransactionEntity = optionalTellerTransaction.get();
+      tellerTransactionEntity.setState(TellerTransaction.State.CANCELED.name());
+      this.tellerTransactionRepository.save(tellerTransactionEntity);
+
+      return cancelTellerTransactionCommand.tellerTransactionIdentifier();
+    } else {
+      this.logger.warn("Teller transaction {} not found", cancelTellerTransactionCommand.tellerTransactionIdentifier());
+    }
+    return null;
+  }
+}
diff --git a/service/src/main/java/io/mifos/teller/service/internal/mapper/TellerMapper.java b/service/src/main/java/io/mifos/teller/service/internal/mapper/TellerMapper.java
index bfb83af..d1902a4 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/mapper/TellerMapper.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/mapper/TellerMapper.java
@@ -28,7 +28,6 @@ public class TellerMapper {
   public static Teller map(final TellerEntity tellerEntity) {
     final Teller teller = new Teller();
     teller.setCode(tellerEntity.getIdentifier());
-    teller.setPassword(tellerEntity.getPassword());
     teller.setTellerAccountIdentifier(tellerEntity.getTellerAccountIdentifier());
     teller.setVaultAccountIdentifier(tellerEntity.getVaultAccountIdentifier());
     teller.setCashdrawLimit(tellerEntity.getCashdrawLimit());
diff --git a/service/src/main/java/io/mifos/teller/service/internal/mapper/TellerTransactionMapper.java b/service/src/main/java/io/mifos/teller/service/internal/mapper/TellerTransactionMapper.java
new file mode 100644
index 0000000..1073219
--- /dev/null
+++ b/service/src/main/java/io/mifos/teller/service/internal/mapper/TellerTransactionMapper.java
@@ -0,0 +1,59 @@
+/*
+ * 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.teller.service.internal.mapper;
+
+import io.mifos.core.lang.DateConverter;
+import io.mifos.teller.api.v1.domain.TellerTransaction;
+import io.mifos.teller.service.internal.repository.TellerTransactionEntity;
+
+public class TellerTransactionMapper {
+
+  private TellerTransactionMapper() {
+    super();
+  }
+
+  public static TellerTransaction map(final TellerTransactionEntity tellerTransactionEntity) {
+    final TellerTransaction tellerTransaction = new TellerTransaction();
+    tellerTransaction.setIdentifier(tellerTransactionEntity.getIdentifier());
+    tellerTransaction.setTransactionType(tellerTransactionEntity.getTransactionType());
+    tellerTransaction.setTransactionDate(DateConverter.toIsoString(tellerTransactionEntity.getTransactionDate()));
+    tellerTransaction.setProductIdentifier(tellerTransactionEntity.getProductIdentifier());
+    tellerTransaction.setProductCaseIdentifier(tellerTransactionEntity.getProductCaseIdentifier());
+    tellerTransaction.setCustomerAccountIdentifier(tellerTransactionEntity.getCustomerAccountIdentifier());
+    tellerTransaction.setTargetAccountIdentifier(tellerTransactionEntity.getTargetAccountIdentifier());
+    tellerTransaction.setAmount(tellerTransactionEntity.getAmount());
+    tellerTransaction.setClerk(tellerTransactionEntity.getClerk());
+    tellerTransaction.setState(tellerTransactionEntity.getState());
+
+    return tellerTransaction;
+  }
+
+  public static TellerTransactionEntity map(final TellerTransaction tellerTransaction) {
+    final TellerTransactionEntity tellerTransactionEntity = new TellerTransactionEntity();
+    tellerTransactionEntity.setIdentifier(tellerTransaction.getIdentifier());
+    tellerTransactionEntity.setTransactionType(tellerTransaction.getTransactionType());
+    tellerTransactionEntity.setTransactionDate(DateConverter.fromIsoString(tellerTransaction.getTransactionDate()));
+    tellerTransactionEntity.setProductIdentifier(tellerTransaction.getProductIdentifier());
+    tellerTransactionEntity.setProductCaseIdentifier(tellerTransaction.getProductCaseIdentifier());
+    tellerTransactionEntity.setCustomerAccountIdentifier(tellerTransaction.getCustomerAccountIdentifier());
+    tellerTransactionEntity.setTargetAccountIdentifier(tellerTransaction.getTargetAccountIdentifier());
+    tellerTransactionEntity.setAmount(tellerTransaction.getAmount());
+    tellerTransactionEntity.setClerk(tellerTransaction.getClerk());
+    tellerTransactionEntity.setState(tellerTransaction.getState());
+
+    return tellerTransactionEntity;
+  }
+}
diff --git a/service/src/main/java/io/mifos/teller/service/internal/processor/TellerTransactionProcessor.java b/service/src/main/java/io/mifos/teller/service/internal/processor/TellerTransactionProcessor.java
new file mode 100644
index 0000000..e9c9329
--- /dev/null
+++ b/service/src/main/java/io/mifos/teller/service/internal/processor/TellerTransactionProcessor.java
@@ -0,0 +1,251 @@
+/*
+ * 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.teller.service.internal.processor;
+
+import io.mifos.accounting.api.v1.domain.Creditor;
+import io.mifos.accounting.api.v1.domain.Debtor;
+import io.mifos.accounting.api.v1.domain.JournalEntry;
+import io.mifos.core.api.util.UserContextHolder;
+import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
+import io.mifos.teller.ServiceConstants;
+import io.mifos.teller.api.v1.domain.Charge;
+import io.mifos.teller.api.v1.domain.TellerTransaction;
+import io.mifos.teller.api.v1.domain.TellerTransactionCosts;
+import io.mifos.teller.service.internal.repository.TellerEntity;
+import io.mifos.teller.service.internal.repository.TellerRepository;
+import io.mifos.teller.service.internal.service.helper.AccountingService;
+import io.mifos.teller.service.internal.service.helper.DepositAccountManagementService;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Component
+public class TellerTransactionProcessor {
+
+  private final Logger logger;
+  private final AccountingService accountingService;
+  private final DepositAccountManagementService depositAccountManagementService;
+  private final TellerRepository tellerRepository;
+
+  @Autowired
+  public TellerTransactionProcessor(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                                    final AccountingService accountingService,
+                                    final DepositAccountManagementService depositAccountManagementService,
+                                    final TellerRepository tellerRepository) {
+    super();
+    this.logger = logger;
+    this.accountingService = accountingService;
+    this.depositAccountManagementService = depositAccountManagementService;
+    this.tellerRepository = tellerRepository;
+  }
+
+  public void process(final String tellerCode, final TellerTransaction tellerTransaction) {
+    switch (tellerTransaction.getTransactionType()) {
+      case ServiceConstants.TX_OPEN_ACCOUNT:
+        this.processDepositAccountOpening(tellerCode, tellerTransaction);
+        break;
+      case ServiceConstants.TX_CLOSE_ACCOUNT:
+        this.processDepositAccountClosing(tellerCode, tellerTransaction);
+        break;
+      case ServiceConstants.TX_ACCOUNT_TRANSFER:
+        this.processTransfer(tellerTransaction);
+        break;
+      case ServiceConstants.TX_CASH_DEPOSIT:
+        this.processCashDeposit(tellerCode, tellerTransaction);
+        break;
+      case ServiceConstants.TX_CASH_WITHDRAWAL:
+        this.processCashWithdrawal(tellerCode, tellerTransaction);
+        break;
+      default:
+        throw new IllegalArgumentException("Unsupported TX type " + tellerTransaction.getTransactionType());
+    }
+  }
+
+  public TellerTransactionCosts getCosts(final TellerTransaction tellerTransaction) {
+    switch (tellerTransaction.getTransactionType()) {
+      case ServiceConstants.TX_OPEN_ACCOUNT:
+      case ServiceConstants.TX_CLOSE_ACCOUNT:
+      case ServiceConstants.TX_ACCOUNT_TRANSFER:
+      case ServiceConstants.TX_CASH_DEPOSIT:
+      case ServiceConstants.TX_CASH_WITHDRAWAL:
+        return this.depositAccountCosts(tellerTransaction);
+      default:
+        throw new IllegalArgumentException("Unsupported TX type " + tellerTransaction.getTransactionType());
+    }
+  }
+
+  private TellerTransactionCosts depositAccountCosts(final TellerTransaction tellerTransaction) {
+    final List<Charge> charges = this.depositAccountManagementService.getCharges(tellerTransaction);
+
+    final TellerTransactionCosts tellerTransactionCosts = new TellerTransactionCosts();
+    tellerTransactionCosts.setCharges(charges);
+    tellerTransactionCosts.setTellerTransactionIdentifier(tellerTransaction.getIdentifier());
+    tellerTransactionCosts.setTotalAmount(tellerTransaction.getAmount() + charges.stream().mapToDouble(Charge::getAmount).sum());
+
+    return tellerTransactionCosts;
+  }
+
+  private void processTransfer(final TellerTransaction tellerTransaction) {
+    final JournalEntry journalEntry = this.prepareJournalEntry(tellerTransaction);
+
+    final TellerTransactionCosts tellerTransactionCosts = this.depositAccountCosts(tellerTransaction);
+
+    final HashSet<Debtor> debtors = new HashSet<>();
+    journalEntry.setDebtors(debtors);
+
+    final Debtor debtor = new Debtor();
+    debtor.setAccountNumber(tellerTransaction.getCustomerAccountIdentifier());
+    debtor.setAmount(tellerTransactionCosts.getTotalAmount().toString());
+    debtors.add(debtor);
+
+    final HashSet<Creditor> creditors = new HashSet<>();
+    journalEntry.setCreditors(creditors);
+
+    final Creditor targetCreditor = new Creditor();
+    targetCreditor.setAccountNumber(tellerTransaction.getTargetAccountIdentifier());
+    targetCreditor.setAmount(tellerTransaction.getAmount().toString());
+    creditors.add(targetCreditor);
+
+    creditors.addAll(this.createChargeCreditors(tellerTransactionCosts));
+
+    this.accountingService.postJournalEntry(journalEntry);
+  }
+
+  private void processCashDeposit(final String tellerCode, final TellerTransaction tellerTransaction) {
+    final Optional<TellerEntity> optionalTeller = this.tellerRepository.findByIdentifier(tellerCode);
+    if (!optionalTeller.isPresent()) {
+      this.logger.warn("Teller {} not found.", tellerCode);
+      throw new IllegalStateException("Teller not found.");
+    }
+
+    final TellerEntity tellerEntity = optionalTeller.get();
+    final JournalEntry journalEntry = this.prepareJournalEntry(tellerTransaction);
+    final TellerTransactionCosts tellerTransactionCosts = this.depositAccountCosts(tellerTransaction);
+
+    final HashSet<Debtor> debtors = new HashSet<>();
+    journalEntry.setDebtors(debtors);
+
+    final Debtor tellerDebtor = new Debtor();
+    tellerDebtor.setAccountNumber(tellerEntity.getTellerAccountIdentifier());
+    tellerDebtor.setAmount(tellerTransaction.getAmount().toString());
+    debtors.add(tellerDebtor);
+
+    final Double chargesTotal = tellerTransactionCosts.getTotalAmount() - tellerTransaction.getAmount();
+    final Debtor customerDebtor = new Debtor();
+    customerDebtor.setAccountNumber(tellerTransaction.getCustomerAccountIdentifier());
+    customerDebtor.setAmount(chargesTotal.toString());
+
+    final HashSet<Creditor> creditors = new HashSet<>();
+    journalEntry.setCreditors(creditors);
+
+    final Creditor customerCreditor = new Creditor();
+    customerCreditor.setAccountNumber(tellerTransaction.getCustomerAccountIdentifier());
+    customerCreditor.setAmount(tellerTransaction.getAmount().toString());
+    creditors.add(customerCreditor);
+
+    creditors.addAll(this.createChargeCreditors(tellerTransactionCosts));
+
+    this.accountingService.postJournalEntry(journalEntry);
+  }
+
+  private void processCashWithdrawal(final String tellerCode, final TellerTransaction tellerTransaction) {
+    final Optional<TellerEntity> optionalTeller = this.tellerRepository.findByIdentifier(tellerCode);
+    if (!optionalTeller.isPresent()) {
+      this.logger.warn("Teller {} not found.", tellerCode);
+      throw new IllegalStateException("Teller not found.");
+    }
+
+    final TellerEntity tellerEntity = optionalTeller.get();
+    final JournalEntry journalEntry = this.prepareJournalEntry(tellerTransaction);
+    final TellerTransactionCosts tellerTransactionCosts = this.depositAccountCosts(tellerTransaction);
+
+    final HashSet<Debtor> debtors = new HashSet<>();
+    journalEntry.setDebtors(debtors);
+
+    final Debtor customerDebtor = new Debtor();
+    customerDebtor.setAccountNumber(tellerTransaction.getCustomerAccountIdentifier());
+    customerDebtor.setAmount(tellerTransactionCosts.getTotalAmount().toString());
+
+    final HashSet<Creditor> creditors = new HashSet<>();
+    journalEntry.setCreditors(creditors);
+
+    final Creditor tellerCreditor = new Creditor();
+    tellerCreditor.setAccountNumber(tellerEntity.getTellerAccountIdentifier());
+    tellerCreditor.setAmount(tellerTransaction.getAmount().toString());
+    creditors.add(tellerCreditor);
+
+    creditors.addAll(this.createChargeCreditors(tellerTransactionCosts));
+
+    this.accountingService.postJournalEntry(journalEntry);
+  }
+
+  private void processDepositAccountClosing(final String tellerCode, final TellerTransaction tellerTransaction) {
+    final List<ProductInstance> productInstances =
+        this.depositAccountManagementService.getProductInstance(tellerTransaction.getCustomerIdentifier());
+
+    this.processCashWithdrawal(tellerCode, tellerTransaction);
+
+    productInstances.forEach(productInstance -> {
+      if (productInstance.getAccountIdentifier().equals(tellerTransaction.getCustomerAccountIdentifier())) {
+        this.depositAccountManagementService.closeProductInstance(tellerTransaction.getCustomerAccountIdentifier());
+        this.accountingService.closeAccount(tellerTransaction.getCustomerAccountIdentifier());
+      }
+    });
+  }
+
+  private void processDepositAccountOpening(final String tellerCode, final TellerTransaction tellerTransaction) {
+    final List<ProductInstance> productInstances =
+        this.depositAccountManagementService.getProductInstance(tellerTransaction.getCustomerIdentifier());
+
+    this.processCashDeposit(tellerCode, tellerTransaction);
+
+    productInstances.forEach(productInstance -> {
+      if (productInstance.getAccountIdentifier().equals(tellerTransaction.getCustomerAccountIdentifier())) {
+        this.depositAccountManagementService.activateProductInstance(tellerTransaction.getCustomerAccountIdentifier());
+        this.accountingService.openAccount(tellerTransaction.getCustomerAccountIdentifier());
+      }
+    });
+  }
+
+  private JournalEntry prepareJournalEntry(final TellerTransaction tellerTransaction) {
+    final JournalEntry journalEntry = new JournalEntry();
+    journalEntry.setTransactionIdentifier(tellerTransaction.getIdentifier());
+    journalEntry.setTransactionDate(tellerTransaction.getTransactionDate());
+    journalEntry.setTransactionType(tellerTransaction.getTransactionType());
+    journalEntry.setClerk(UserContextHolder.checkedGetUser());
+
+    return journalEntry;
+  }
+
+  private Set<Creditor> createChargeCreditors(final TellerTransactionCosts tellerTransactionCosts) {
+    return tellerTransactionCosts.getCharges()
+        .stream()
+        .map(charge -> {
+          final Creditor chargeCreditor = new Creditor();
+          chargeCreditor.setAccountNumber(charge.getIncomeAccountIdentifier());
+          chargeCreditor.setAmount(charge.getAmount().toString());
+          return chargeCreditor;
+        })
+        .collect(Collectors.toSet());
+  }
+}
diff --git a/api/src/main/java/io/mifos/teller/api/v1/domain/TellerTransaction.java b/service/src/main/java/io/mifos/teller/service/internal/repository/TellerTransactionEntity.java
similarity index 55%
copy from api/src/main/java/io/mifos/teller/api/v1/domain/TellerTransaction.java
copy to service/src/main/java/io/mifos/teller/service/internal/repository/TellerTransactionEntity.java
index 5dc8ffa..f880906 100644
--- a/api/src/main/java/io/mifos/teller/api/v1/domain/TellerTransaction.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/repository/TellerTransactionEntity.java
@@ -13,46 +13,76 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.mifos.teller.api.v1.domain;
-
-import io.mifos.core.lang.validation.constraints.ValidIdentifier;
-
-import javax.validation.constraints.DecimalMin;
-import javax.validation.constraints.NotNull;
-
-public class TellerTransaction {
-
-  public enum State {
-    PENDING,
-    CANCELED,
-    CONFIRMED
-  }
-
-  @ValidIdentifier(optional = true)
+package io.mifos.teller.service.internal.repository;
+
+import io.mifos.core.mariadb.util.LocalDateTimeConverter;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "tajet_teller_transactions")
+public class TellerTransactionEntity {
+
+  @Id
+  @GeneratedValue(strategy = GenerationType.IDENTITY)
+  @Column(name = "id", nullable = false)
+  private Long id;
+  @ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+  @JoinColumn(name = "teller_id")
+  private TellerEntity teller;
+  @Column(name = "identifier", nullable = false, length = 32)
   private String identifier;
-  @NotNull
+  @Column(name = "transaction_type", nullable = false, length = 32)
   private String transactionType;
-  @NotNull
-  private String transactionDate;
-  @ValidIdentifier
+  @Column(name = "transaction_date", nullable = false)
+  @Convert(converter = LocalDateTimeConverter.class)
+  private LocalDateTime transactionDate;
+  @Column(name = "product_identifier", nullable = false, length = 32)
   private String productIdentifier;
-  @ValidIdentifier(optional = true)
+  @Column(name = "product_case_identifier", nullable = true, length = 32)
   private String productCaseIdentifier;
-  @ValidIdentifier
+  @Column(name = "customer_account_identifier", nullable = false, length = 32)
   private String customerAccountIdentifier;
-  @ValidIdentifier(optional = true)
+  @Column(name = "target_account_identifier", nullable = true, length = 32)
   private String targetAccountIdentifier;
-  @ValidIdentifier
+  @Column(name = "clerk", nullable = false, length = 32)
   private String clerk;
-  @NotNull
-  @DecimalMin("0.00")
+  @Column(name = "amount", nullable = false)
   private Double amount;
-  private State state;
+  @Column(name = "a_state", nullable = false, length = 256)
+  private String state;
 
-  public TellerTransaction() {
+  public TellerTransactionEntity() {
     super();
   }
 
+  public Long getId() {
+    return this.id;
+  }
+
+  public void setId(final Long id) {
+    this.id = id;
+  }
+
+  public TellerEntity getTeller() {
+    return this.teller;
+  }
+
+  public void setTeller(final TellerEntity teller) {
+    this.teller = teller;
+  }
+
   public String getIdentifier() {
     return this.identifier;
   }
@@ -69,11 +99,11 @@ public class TellerTransaction {
     this.transactionType = transactionType;
   }
 
-  public String getTransactionDate() {
+  public LocalDateTime getTransactionDate() {
     return this.transactionDate;
   }
 
-  public void setTransactionDate(final String transactionDate) {
+  public void setTransactionDate(final LocalDateTime transactionDate) {
     this.transactionDate = transactionDate;
   }
 
@@ -126,10 +156,10 @@ public class TellerTransaction {
   }
 
   public String getState() {
-    return this.state.name();
+    return this.state;
   }
 
   public void setState(final String state) {
-    this.state = State.valueOf(state);
+    this.state = state;
   }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/internal/repository/TellerTransactionRepository.java b/service/src/main/java/io/mifos/teller/service/internal/repository/TellerTransactionRepository.java
new file mode 100644
index 0000000..aea3864
--- /dev/null
+++ b/service/src/main/java/io/mifos/teller/service/internal/repository/TellerTransactionRepository.java
@@ -0,0 +1,32 @@
+/*
+ * 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.teller.service.internal.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface TellerTransactionRepository extends JpaRepository<TellerTransactionEntity, Long> {
+  Optional<TellerTransactionEntity> findByIdentifier(final String tellerTransactionIdentifier);
+
+  List<TellerTransactionEntity> findByTellerOrderByTransactionDateAsc(final TellerEntity teller);
+
+  List<TellerTransactionEntity> findByTellerAndStateOrderByTransactionDateAsc(final TellerEntity teller,
+                                                                              final String state);
+}
diff --git a/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java b/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
index 220abb6..da7f2ae 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/service/TellerOperationService.java
@@ -15,8 +15,61 @@
  */
 package io.mifos.teller.service.internal.service;
 
+import io.mifos.core.lang.ServiceException;
+import io.mifos.teller.ServiceConstants;
+import io.mifos.teller.api.v1.domain.TellerTransaction;
+import io.mifos.teller.service.internal.mapper.TellerTransactionMapper;
+import io.mifos.teller.service.internal.repository.TellerEntity;
+import io.mifos.teller.service.internal.repository.TellerRepository;
+import io.mifos.teller.service.internal.repository.TellerTransactionRepository;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+
 @Service
 public class TellerOperationService {
+
+  private final Logger logger;
+  private final TellerRepository tellerRepository;
+  private final TellerTransactionRepository tellerTransactionRepository;
+
+  @Autowired
+  public TellerOperationService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                                final TellerRepository tellerRepository,
+                                final TellerTransactionRepository tellerTransactionRepository) {
+    super();
+    this.logger = logger;
+    this.tellerRepository = tellerRepository;
+    this.tellerTransactionRepository = tellerTransactionRepository;
+  }
+
+  public boolean tellerTransactionExists(final String tellerTransactionIdentifier) {
+    return this.tellerTransactionRepository.findByIdentifier(tellerTransactionIdentifier).isPresent();
+  }
+
+  public List<TellerTransaction> fetchTellerTransactions(final String tellerCode, final String state) {
+    final Optional<TellerEntity> optionalTellerEntity = this.tellerRepository.findByIdentifier(tellerCode);
+    if (optionalTellerEntity.isPresent()) {
+      final TellerEntity tellerEntity = optionalTellerEntity.get();
+      if (state != null) {
+        return this.tellerTransactionRepository.findByTellerAndStateOrderByTransactionDateAsc(tellerEntity, state)
+            .stream()
+            .map(TellerTransactionMapper::map)
+            .collect(Collectors.toList());
+      } else {
+        return this.tellerTransactionRepository.findByTellerOrderByTransactionDateAsc(tellerEntity)
+            .stream()
+            .map(TellerTransactionMapper::map)
+            .collect(Collectors.toList());
+      }
+    } else {
+      throw ServiceException.notFound("Teller {0} not found.", tellerCode);
+    }
+  }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/internal/service/helper/AccountingService.java b/service/src/main/java/io/mifos/teller/service/internal/service/helper/AccountingService.java
index d64decb..2926910 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/service/helper/AccountingService.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/service/helper/AccountingService.java
@@ -17,6 +17,7 @@ package io.mifos.teller.service.internal.service.helper;
 
 import io.mifos.accounting.api.v1.client.AccountNotFoundException;
 import io.mifos.accounting.api.v1.client.LedgerManager;
+import io.mifos.accounting.api.v1.domain.AccountCommand;
 import io.mifos.accounting.api.v1.domain.AccountEntryPage;
 import io.mifos.accounting.api.v1.domain.JournalEntry;
 import io.mifos.teller.ServiceConstants;
@@ -59,4 +60,18 @@ public class AccountingService {
   public void postJournalEntry(final JournalEntry journalEntry) {
     this.ledgerManager.createJournalEntry(journalEntry);
   }
+
+  public void closeAccount(final String accountIdentifier) {
+    final AccountCommand accountCommand = new AccountCommand();
+    accountCommand.setAction(AccountCommand.Action.CLOSE.name());
+    accountCommand.setComment(ServiceConstants.TX_CLOSE_ACCOUNT);
+    this.ledgerManager.accountCommand(accountIdentifier, accountCommand);
+  }
+
+  public void openAccount(final String accountIdentifier) {
+    final AccountCommand accountCommand = new AccountCommand();
+    accountCommand.setAction(AccountCommand.Action.REOPEN.name());
+    accountCommand.setComment(ServiceConstants.TX_OPEN_ACCOUNT);
+    this.ledgerManager.accountCommand(accountIdentifier, accountCommand);
+  }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/internal/service/helper/DepositAccountManagementService.java b/service/src/main/java/io/mifos/teller/service/internal/service/helper/DepositAccountManagementService.java
index 4d0a458..5c8a0aa 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/service/helper/DepositAccountManagementService.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/service/helper/DepositAccountManagementService.java
@@ -15,13 +15,23 @@
  */
 package io.mifos.teller.service.internal.service.helper;
 
+import io.mifos.deposit.api.v1.EventConstants;
 import io.mifos.deposit.api.v1.client.DepositAccountManager;
+import io.mifos.deposit.api.v1.definition.domain.Action;
+import io.mifos.deposit.api.v1.definition.domain.ProductDefinition;
+import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
 import io.mifos.teller.ServiceConstants;
+import io.mifos.teller.api.v1.domain.Charge;
+import io.mifos.teller.api.v1.domain.TellerTransaction;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
 @Service
 public class DepositAccountManagementService {
 
@@ -35,4 +45,46 @@ public class DepositAccountManagementService {
     this.logger = logger;
     this.depositAccountManager = depositAccountManager;
   }
+
+  public List<ProductInstance> getProductInstance(final String customerIdentifier) {
+    return this.depositAccountManager.fetchProductInstances(customerIdentifier);
+  }
+
+  public List<Charge> getCharges(final TellerTransaction tellerTransaction) {
+    final List<Charge> charges = new ArrayList<>();
+    final ProductDefinition productDefinition =
+        this.depositAccountManager.findProductDefinition(tellerTransaction.getProductIdentifier());
+    final List<Action> actions = this.depositAccountManager.fetchActions();
+
+    final HashMap<String, Action> mappedActions = new HashMap<>(actions.size());
+    actions.forEach(action -> mappedActions.put(action.getIdentifier(), action));
+
+    final List<io.mifos.deposit.api.v1.definition.domain.Charge> productCharges = productDefinition.getCharges();
+    productCharges.forEach(productCharge -> {
+      final Action action = mappedActions.get(productCharge.getActionIdentifier());
+      if (action != null
+          && action.getTransactionType().equals(tellerTransaction.getTransactionType())) {
+        final Charge charge = new Charge();
+        charge.setCode(productCharge.getActionIdentifier());
+        charge.setIncomeAccountIdentifier(productCharge.getIncomeAccountIdentifier());
+        charge.setName(productCharge.getName());
+        if (productCharge.getProportional()) {
+          final Double amount = tellerTransaction.getAmount();
+          charge.setAmount(amount / 100.0D * productCharge.getAmount());
+        } else {
+          charge.setAmount(productCharge.getAmount());
+        }
+        charges.add(charge);
+      }
+    });
+    return charges;
+  }
+
+  public void activateProductInstance(final String customerAccountIdentifier) {
+    this.depositAccountManager.postProductInstanceCommand(customerAccountIdentifier, EventConstants.ACTIVATE_PRODUCT_INSTANCE);
+  }
+
+  public void closeProductInstance(final String customerAccountIdentifier) {
+    this.depositAccountManager.postProductInstanceCommand(customerAccountIdentifier, EventConstants.CLOSE_PRODUCT_INSTANCE);
+  }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/rest/TellerOperationRestController.java b/service/src/main/java/io/mifos/teller/service/rest/TellerOperationRestController.java
index 303428d..611b485 100644
--- a/service/src/main/java/io/mifos/teller/service/rest/TellerOperationRestController.java
+++ b/service/src/main/java/io/mifos/teller/service/rest/TellerOperationRestController.java
@@ -15,20 +15,25 @@
  */
 package io.mifos.teller.service.rest;
 
-import io.mifos.accounting.api.v1.client.LedgerManager;
 import io.mifos.anubis.annotation.AcceptedTokenType;
 import io.mifos.anubis.annotation.Permittable;
+import io.mifos.core.api.util.UserContextHolder;
 import io.mifos.core.command.gateway.CommandGateway;
 import io.mifos.core.lang.ServiceException;
-import io.mifos.deposit.api.v1.client.DepositAccountManager;
-import io.mifos.portfolio.api.v1.client.PortfolioManager;
 import io.mifos.teller.ServiceConstants;
 import io.mifos.teller.api.v1.PermittableGroupIds;
+import io.mifos.teller.api.v1.domain.Teller;
 import io.mifos.teller.api.v1.domain.TellerAuthentication;
 import io.mifos.teller.api.v1.domain.TellerTransaction;
 import io.mifos.teller.api.v1.domain.TellerTransactionCosts;
+import io.mifos.teller.service.internal.command.AuthenticateTellerCommand;
+import io.mifos.teller.service.internal.command.CancelTellerTransactionCommand;
+import io.mifos.teller.service.internal.command.ConfirmTellerTransactionCommand;
+import io.mifos.teller.service.internal.command.InitializeTellerTransactionCommand;
+import io.mifos.teller.service.internal.command.PauseTellerCommand;
 import io.mifos.teller.service.internal.service.TellerManagementService;
 import io.mifos.teller.service.internal.service.TellerOperationService;
+import io.mifos.teller.service.internal.service.helper.AccountingService;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -43,8 +48,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.validation.Valid;
-import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 
 @RestController
 @RequestMapping("/teller/{tellerCode}")
@@ -54,25 +59,19 @@ public class TellerOperationRestController {
   private final CommandGateway commandGateway;
   private final TellerOperationService tellerOperationService;
   private final TellerManagementService tellerManagementService;
-  private final LedgerManager ledgerManager;
-  private final DepositAccountManager depositAccountManager;
-  private final PortfolioManager portfolioManager;
+  private final AccountingService accountingService;
 
   @Autowired
   public TellerOperationRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
                                        final CommandGateway commandGateway,
                                        final TellerOperationService tellerOperationService,
                                        final TellerManagementService tellerManagementService,
-                                       final LedgerManager ledgerManager,
-                                       final DepositAccountManager depositAccountManager,
-                                       final PortfolioManager portfolioManager) {
+                                       final AccountingService accountingService) {
     this.logger = logger;
     this.commandGateway = commandGateway;
     this.tellerOperationService = tellerOperationService;
     this.tellerManagementService = tellerManagementService;
-    this.ledgerManager = ledgerManager;
-    this.depositAccountManager = depositAccountManager;
-    this.portfolioManager = portfolioManager;
+    this.accountingService = accountingService;
   }
 
   @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.TELLER_OPERATION)
@@ -83,9 +82,23 @@ public class TellerOperationRestController {
       produces = MediaType.APPLICATION_JSON_VALUE
   )
   @ResponseBody
-  ResponseEntity<Void> auth(@PathVariable("tellerCode") final String tellerCode,
+  ResponseEntity<String> auth(@PathVariable("tellerCode") final String tellerCode,
                             @RequestBody @Valid final TellerAuthentication tellerAuthentication) {
-    return ResponseEntity.accepted().build();
+    final Teller teller = this.verifyTeller(tellerCode);
+
+    if (!teller.getAssignedEmployee().equals(tellerAuthentication.getEmployeeIdentifier())) {
+      throw ServiceException.badRequest("User {0} is not assigned to teller.", tellerAuthentication.getEmployeeIdentifier());
+    }
+
+    this.verifyEmployee(teller);
+
+    try {
+      return ResponseEntity.ok(
+          this.commandGateway.process(new AuthenticateTellerCommand(tellerCode, tellerAuthentication), String.class).get()
+      );
+    } catch (final Exception e) {
+      throw ServiceException.notFound("Teller {0} not found.", teller.getCode());
+    }
   }
 
   @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.TELLER_OPERATION)
@@ -98,6 +111,22 @@ public class TellerOperationRestController {
   @ResponseBody
   ResponseEntity<Void> post(@PathVariable("tellerCode") final String tellerCode,
                             @RequestParam(value = "command", required = true) final String command) {
+
+    final Teller teller = this.verifyTeller(tellerCode);
+
+    this.verifyEmployee(teller);
+
+    switch (command.toUpperCase()) {
+      case "PAUSE":
+        if (!teller.getState().equals(Teller.State.ACTIVE.name())) {
+          throw ServiceException.badRequest("Teller {0} is not active.", tellerCode);
+        }
+
+        this.commandGateway.process(new PauseTellerCommand(tellerCode));
+        break;
+      default :
+        throw ServiceException.badRequest("Unknonw command {0}", command);
+    }
     return ResponseEntity.accepted().build();
   }
 
@@ -111,7 +140,31 @@ public class TellerOperationRestController {
   @ResponseBody
   ResponseEntity<TellerTransactionCosts> post(@PathVariable("tellerCode") final String tellerCode,
                                               @RequestBody @Valid final TellerTransaction tellerTransaction) {
-    throw ServiceException.notFound("Teller {0} not found.", tellerCode);
+    final Teller teller = this.verifyTeller(tellerCode);
+
+    this.verifyEmployee(teller);
+
+    if (!teller.getState().equals(Teller.State.ACTIVE.name())) {
+      throw ServiceException.conflict("Teller {0} ist not active.", tellerCode);
+    }
+
+    if (!this.accountingService.accountExists(tellerTransaction.getCustomerAccountIdentifier())) {
+      throw ServiceException.badRequest("Customer account {0} not found.");
+    }
+
+    if (tellerTransaction.getTargetAccountIdentifier() != null &&
+        !this.accountingService.accountExists(tellerTransaction.getTargetAccountIdentifier())) {
+      throw ServiceException.badRequest("Target account {0} not found.");
+    }
+
+    try {
+      return ResponseEntity.ok(
+          this.commandGateway.process(
+              new InitializeTellerTransactionCommand(tellerCode, tellerTransaction), TellerTransactionCosts.class).get()
+      );
+    } catch (final Exception e) {
+      throw ServiceException.badRequest("Transaction for teller {0} not valid.", tellerCode);
+    }
   }
 
   @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.TELLER_OPERATION)
@@ -125,6 +178,25 @@ public class TellerOperationRestController {
   ResponseEntity<Void> confirm(@PathVariable("tellerCode") final String tellerCode,
                                @PathVariable("identifier") final String tellerTransactionIdentifier,
                                @RequestParam(value = "command", required = true) final String command) {
+    final Teller teller = this.verifyTeller(tellerCode);
+
+    this.verifyEmployee(teller);
+
+    if (!this.tellerOperationService.tellerTransactionExists(tellerTransactionIdentifier)) {
+      throw ServiceException.notFound("Transaction {0} not found.", tellerTransactionIdentifier);
+    }
+
+    switch (command.toUpperCase()) {
+      case "CONFIRM" :
+        this.commandGateway.process(new ConfirmTellerTransactionCommand(tellerTransactionIdentifier));
+        break;
+      case "CANCEL" :
+        this.commandGateway.process(new CancelTellerTransactionCommand(tellerTransactionIdentifier));
+        break;
+      default :
+        throw ServiceException.badRequest("Unsupported teller transaction command {0}.", command);
+    }
+
     return ResponseEntity.accepted().build();
   }
 
@@ -138,6 +210,25 @@ public class TellerOperationRestController {
   @ResponseBody
   ResponseEntity<List<TellerTransaction>> fetch(@PathVariable("tellerCode") final String tellerCode,
                                                 @RequestParam(value = "status", required = false) final String status) {
-    return ResponseEntity.ok(Collections.emptyList());
+    this.verifyTeller(tellerCode);
+    return ResponseEntity.ok(
+        this.tellerOperationService.fetchTellerTransactions(tellerCode, status)
+    );
+  }
+
+  private Teller verifyTeller(final String tellerCode) {
+    final Optional<Teller> optionalTeller = this.tellerManagementService.findByIdentifier(tellerCode);
+    if (!optionalTeller.isPresent()) {
+      throw ServiceException.notFound("Teller {0} not found.", tellerCode);
+    } else {
+      return optionalTeller.get();
+    }
+  }
+
+  private void verifyEmployee(final Teller teller) {
+    final String currentUser = UserContextHolder.checkedGetUser();
+    if (!currentUser.equals(teller.getAssignedEmployee())) {
+      throw ServiceException.badRequest("User {0} is not assigned to teller {1}", currentUser, teller.getCode());
+    }
   }
 }
diff --git a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
index 717b5da..5b4556d 100644
--- a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
+++ b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
@@ -15,8 +15,8 @@
 --
 
 CREATE TABLE tajet_teller (
-  id BIGINT NOT NULL AUTO_INCREMENT,
-  identifier                   VARCHAR(32)     NOT NULL,
+  id                           BIGINT         NOT NULL AUTO_INCREMENT,
+  identifier                   VARCHAR(32)    NOT NULL,
   a_password                   VARCHAR(4096)  NOT NULL,
   a_salt                       VARCHAR(4069)  NOT NULL,
   office_identifier            VARCHAR(32)    NOT NULL,
@@ -32,3 +32,21 @@ CREATE TABLE tajet_teller (
   CONSTRAINT tajet_teller_pk PRIMARY KEY (id),
   CONSTRAINT tajet_teller_identifier_uq UNIQUE (identifier)
 );
+
+CREATE TABLE tajet_teller_transactions (
+  id                          BIGINT         NOT NULL AUTO_INCREMENT,
+  teller_id                   BIGINT         NOT NULL,
+  identifier                  VARCHAR(32)    NOT NULL,
+  transaction_type            VARCHAR(32)    NOT NULL,
+  transaction_date            TIMESTAMP(3)   NOT NULL,
+  product_identifier          VARCHAR(32)    NOT NULL,
+  product_case_identifier     VARCHAR(32)    NULL,
+  customer_account_identifier VARCHAR(32)    NOT NULL,
+  target_account_identifier   VARCHAR(32)    NULL,
+  clerk                       VARCHAR(32)    NOT NULL,
+  amount                      NUMERIC(15, 5) NOT NULL,
+  a_state                     VARCHAR(256)   NOT NULL,
+  CONSTRAINT tajet_teller_transaction PRIMARY KEY (id),
+  CONSTRAINT tajet_teller_identifier_uq UNIQUE (identifier),
+  CONSTRAINT tajet_teller_transaction_fk FOREIGN KEY (teller_id) REFERENCES tajet_teller (id)
+);

-- 
To stop receiving notification emails like this one, please contact
myrle@apache.org.

Mime
View raw message