You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by my...@apache.org on 2018/01/22 15:32:22 UTC

[fineract-cn-teller] 24/30: added account state validation before any transaction added ability to include charges in cash received

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 4da5eddb976af4c75d69b9acee772e8f80e02643
Author: mgeiss <mg...@mifos.org>
AuthorDate: Thu Jul 6 11:06:22 2017 +0200

    added account state validation before any transaction
    added ability to include charges in cash received
---
 .../mifos/teller/api/v1/client/TellerManager.java  |   3 +-
 .../java/io/mifos/teller/TestTellerOperation.java  | 129 +++++++++---
 .../mifos/teller/listener/TellerEventListener.java |  11 +
 .../command/ConfirmTellerTransactionCommand.java   |   8 +-
 .../handler/TellerTransactionAggregate.java        |   3 +-
 ...ocessor.java => DepositTransactionHandler.java} | 146 ++++++--------
 .../processor/TellerTransactionProcessor.java      | 224 +--------------------
 .../rest/TellerOperationRestController.java        |  15 +-
 8 files changed, 204 insertions(+), 335 deletions(-)

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 82529b3..b31001e 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
@@ -169,7 +169,8 @@ public interface TellerManager {
   })
   void confirm(@PathVariable("tellerCode") final String tellerCode,
                @PathVariable("identifier") final String tellerTransactionIdentifier,
-               @RequestParam(value = "command", required = true) final String command);
+               @RequestParam(value = "command", required = true) final String command,
+               @RequestParam(value = "charges", required = false, defaultValue = "excluded") final String charges);
 
   @RequestMapping(
       value = "/teller/{tellerCode}/transactions",
diff --git a/component-test/src/main/java/io/mifos/teller/TestTellerOperation.java b/component-test/src/main/java/io/mifos/teller/TestTellerOperation.java
index 1f58971..98cbfbc 100644
--- a/component-test/src/main/java/io/mifos/teller/TestTellerOperation.java
+++ b/component-test/src/main/java/io/mifos/teller/TestTellerOperation.java
@@ -16,14 +16,19 @@
 package io.mifos.teller;
 
 import io.mifos.accounting.api.v1.domain.Account;
+import io.mifos.core.api.context.AutoUserContext;
 import io.mifos.core.lang.DateConverter;
+import io.mifos.deposit.api.v1.definition.domain.ProductDefinition;
+import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
 import io.mifos.teller.api.v1.EventConstants;
 import io.mifos.teller.api.v1.client.TellerNotFoundException;
+import io.mifos.teller.api.v1.client.TellerTransactionValidationException;
 import io.mifos.teller.api.v1.client.TellerValidationException;
 import io.mifos.teller.api.v1.client.TransactionProcessingException;
 import io.mifos.teller.api.v1.domain.Teller;
 import io.mifos.teller.api.v1.domain.TellerManagementCommand;
 import io.mifos.teller.api.v1.domain.TellerTransaction;
+import io.mifos.teller.api.v1.domain.TellerTransactionCosts;
 import io.mifos.teller.api.v1.domain.UnlockDrawerCommand;
 import io.mifos.teller.util.TellerGenerator;
 import org.apache.commons.lang3.RandomStringUtils;
@@ -39,6 +44,8 @@ import java.util.Optional;
 
 public class TestTellerOperation extends AbstractTellerTest {
 
+  private static Teller tellerUnderTest = null;
+
   public TestTellerOperation() {
     super();
   }
@@ -94,13 +101,6 @@ public class TestTellerOperation extends AbstractTellerTest {
     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();
@@ -331,37 +331,49 @@ public class TestTellerOperation extends AbstractTellerTest {
     super.testSubject.post(teller.getCode(), tellerTransaction);
   }
 
-
   private Teller prepareTeller() throws Exception {
-    final String officeIdentifier = RandomStringUtils.randomAlphabetic(32);
-    final Teller teller = TellerGenerator.createRandomTeller();
+    if (TestTellerOperation.tellerUnderTest == null) {
+      final String officeIdentifier = RandomStringUtils.randomAlphabetic(32);
+      TestTellerOperation.tellerUnderTest = TellerGenerator.createRandomTeller();
 
-    Mockito.doAnswer(invocation -> true)
-        .when(super.organizationServiceSpy).officeExists(Matchers.eq(officeIdentifier));
+      Mockito.doAnswer(invocation -> true)
+          .when(super.organizationServiceSpy).officeExists(Matchers.eq(officeIdentifier));
 
-    Mockito.doAnswer(invocation -> Optional.of(new Account()))
-        .when(super.accountingServiceSpy).findAccount(Matchers.eq(teller.getTellerAccountIdentifier()));
+      Mockito.doAnswer(invocation -> Optional.of(new Account()))
+          .when(super.accountingServiceSpy).findAccount(Matchers.eq(TestTellerOperation.tellerUnderTest.getTellerAccountIdentifier()));
 
-    Mockito.doAnswer(invocation -> Optional.of(new Account()))
-        .when(super.accountingServiceSpy).findAccount(Matchers.eq(teller.getVaultAccountIdentifier()));
+      Mockito.doAnswer(invocation -> Optional.of(new Account()))
+          .when(super.accountingServiceSpy).findAccount(Matchers.eq(TestTellerOperation.tellerUnderTest.getVaultAccountIdentifier()));
 
-    super.testSubject.create(officeIdentifier, teller);
+      super.testSubject.create(officeIdentifier, TestTellerOperation.tellerUnderTest);
 
-    Assert.assertTrue(super.eventRecorder.wait(EventConstants.POST_TELLER, teller.getCode()));
+      Assert.assertTrue(super.eventRecorder.wait(EventConstants.POST_TELLER, TestTellerOperation.tellerUnderTest.getCode()));
 
-    final TellerManagementCommand command = new TellerManagementCommand();
-    command.setAction(TellerManagementCommand.Action.OPEN.name());
-    command.setAdjustment(TellerManagementCommand.Adjustment.NONE.name());
-    command.setAssignedEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+      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()));
+      Mockito.doAnswer(invocation -> true)
+          .when(super.organizationServiceSpy).employeeExists(Matchers.eq(command.getAssignedEmployeeIdentifier()));
+
+      super.testSubject.post(officeIdentifier, TestTellerOperation.tellerUnderTest.getCode(), command);
 
-    super.testSubject.post(officeIdentifier, teller.getCode(), command);
+      Assert.assertTrue(super.eventRecorder.wait(EventConstants.OPEN_TELLER, TestTellerOperation.tellerUnderTest.getCode()));
+    }
 
-    Assert.assertTrue(super.eventRecorder.wait(EventConstants.OPEN_TELLER, teller.getCode()));
+    final ProductInstance productInstance = new ProductInstance();
+    productInstance.setProductIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    productInstance.setBalance(0.00D);
+    Mockito.doAnswer(invocation -> productInstance)
+        .when(super.depositAccountManagementServiceSpy).findProductInstance(Matchers.anyString());
 
-    return teller;
+    final ProductDefinition productDefinition = new ProductDefinition();
+    productDefinition.setMinimumBalance(0.00D);
+    Mockito.doAnswer(invocation -> productDefinition)
+        .when(super.depositAccountManagementServiceSpy).findProductDefinition(Matchers.eq(productInstance.getProductIdentifier()));
+
+    return TestTellerOperation.tellerUnderTest;
   }
 
   @Test(expected = TellerNotFoundException.class)
@@ -388,4 +400,67 @@ public class TestTellerOperation extends AbstractTellerTest {
 
     super.testSubject.unlockDrawer(teller.getCode(), unlockDrawerCommand);
   }
+
+  @Test(expected = TellerTransactionValidationException.class)
+  public void shouldNotReopenAccountClosed() throws Exception {
+    final Teller teller = this.prepareTeller();
+
+    final UnlockDrawerCommand unlockDrawerCommand = new UnlockDrawerCommand();
+    unlockDrawerCommand.setEmployeeIdentifier(AbstractTellerTest.TEST_USER);
+    unlockDrawerCommand.setPassword(teller.getPassword());
+
+    super.testSubject.unlockDrawer(teller.getCode(), unlockDrawerCommand);
+
+    super.eventRecorder.wait(EventConstants.AUTHENTICATE_TELLER, teller.getCode());
+
+    final TellerTransaction openAccountTransaction =  new TellerTransaction();
+    openAccountTransaction.setTransactionType(ServiceConstants.TX_OPEN_ACCOUNT);
+    openAccountTransaction.setTransactionDate(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    openAccountTransaction.setProductIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    openAccountTransaction.setCustomerAccountIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    openAccountTransaction.setCustomerIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    openAccountTransaction.setClerk(AbstractTellerTest.TEST_USER);
+    openAccountTransaction.setAmount(1234.56D);
+
+    Mockito.doAnswer(invocation -> Optional.of(new Account()))
+        .when(super.accountingServiceSpy).findAccount(openAccountTransaction.getCustomerAccountIdentifier());
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).getCharges(Matchers.eq(openAccountTransaction));
+    Mockito.doAnswer(invocation -> Collections.emptyList())
+        .when(super.depositAccountManagementServiceSpy).fetchProductInstances(openAccountTransaction.getCustomerIdentifier());
+
+    final TellerTransactionCosts openingCosts = super.testSubject.post(teller.getCode(), openAccountTransaction);
+    super.testSubject.confirm(teller.getCode(), openingCosts.getTellerTransactionIdentifier(), "CONFIRM", "excluded");
+    super.eventRecorder.wait(EventConstants.CONFIRM_TRANSACTION, openingCosts.getTellerTransactionIdentifier());
+
+    final TellerTransaction closeAccountTransaction =  new TellerTransaction();
+    closeAccountTransaction.setTransactionType(ServiceConstants.TX_CLOSE_ACCOUNT);
+    closeAccountTransaction.setTransactionDate(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    closeAccountTransaction.setProductIdentifier(openAccountTransaction.getProductIdentifier());
+    closeAccountTransaction.setCustomerAccountIdentifier(openAccountTransaction.getCustomerAccountIdentifier());
+    closeAccountTransaction.setCustomerIdentifier(openAccountTransaction.getCustomerIdentifier());
+    closeAccountTransaction.setClerk(AbstractTellerTest.TEST_USER);
+    closeAccountTransaction.setAmount(1234.56D);
+
+    final Account account = new Account();
+    account.setBalance(1234.56D);
+
+    Mockito.doAnswer(invocation -> Optional.of(account))
+        .when(super.accountingServiceSpy).findAccount(openAccountTransaction.getCustomerAccountIdentifier());
+
+    final TellerTransactionCosts closingCosts = super.testSubject.post(teller.getCode(), closeAccountTransaction);
+    super.testSubject.confirm(teller.getCode(), closingCosts.getTellerTransactionIdentifier(), "CONFIRM", "excluded");
+    super.eventRecorder.wait(EventConstants.CONFIRM_TRANSACTION, closingCosts.getTellerTransactionIdentifier());
+
+    final TellerTransaction reopenAccountTransaction =  new TellerTransaction();
+    reopenAccountTransaction.setTransactionType(ServiceConstants.TX_OPEN_ACCOUNT);
+    reopenAccountTransaction.setTransactionDate(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    reopenAccountTransaction.setProductIdentifier(openAccountTransaction.getProductIdentifier());
+    reopenAccountTransaction.setCustomerAccountIdentifier(openAccountTransaction.getCustomerAccountIdentifier());
+    reopenAccountTransaction.setCustomerIdentifier(openAccountTransaction.getCustomerIdentifier());
+    reopenAccountTransaction.setClerk(AbstractTellerTest.TEST_USER);
+    reopenAccountTransaction.setAmount(1234.56D);
+
+    super.testSubject.post(teller.getCode(), reopenAccountTransaction);
+  }
 }
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 0ad678a..a334311 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
@@ -103,4 +103,15 @@ public class TellerEventListener {
     this.logger.debug("Teller {} created.", payload);
     this.eventRecorder.event(tenant, EventConstants.PAUSE_TELLER, payload, String.class);
   }
+
+  @JmsListener(
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_CONFIRM_TRANSACTION,
+      subscription = EventConstants.DESTINATION
+  )
+  public void confirmTransaction(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                      final String payload) {
+    this.logger.debug("Teller {} created.", payload);
+    this.eventRecorder.event(tenant, EventConstants.CONFIRM_TRANSACTION, payload, String.class);
+  }
 }
diff --git a/service/src/main/java/io/mifos/teller/service/internal/command/ConfirmTellerTransactionCommand.java b/service/src/main/java/io/mifos/teller/service/internal/command/ConfirmTellerTransactionCommand.java
index 1da91da..32b156e 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/command/ConfirmTellerTransactionCommand.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/command/ConfirmTellerTransactionCommand.java
@@ -17,13 +17,19 @@ package io.mifos.teller.service.internal.command;
 
 public class ConfirmTellerTransactionCommand {
   private final String tellerTransactionIdentifier;
+  private final boolean chargesIncluded;
 
-  public ConfirmTellerTransactionCommand(final String tellerTransactionIdentifier) {
+  public ConfirmTellerTransactionCommand(final String tellerTransactionIdentifier, final String charges) {
     super();
     this.tellerTransactionIdentifier = tellerTransactionIdentifier;
+    this.chargesIncluded = charges.equalsIgnoreCase("included");
   }
 
   public String tellerTransactionIdentifier() {
     return this.tellerTransactionIdentifier;
   }
+
+  public boolean chargesIncluded() {
+    return this.chargesIncluded;
+  }
 }
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
index 5d59db4..224056c 100644
--- 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
@@ -91,7 +91,8 @@ public class TellerTransactionAggregate {
       final TellerTransactionEntity tellerTransactionEntity = optionalTellerTransaction.get();
 
       this.tellerTransactionProcessor.process(tellerTransactionEntity.getTeller().getIdentifier(),
-          TellerTransactionMapper.map(tellerTransactionEntity));
+          TellerTransactionMapper.map(tellerTransactionEntity),
+          confirmTellerTransactionCommand.chargesIncluded());
 
       tellerTransactionEntity.setState(TellerTransaction.State.CONFIRMED.name());
       this.tellerTransactionRepository.save(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/DepositTransactionHandler.java
similarity index 67%
copy from service/src/main/java/io/mifos/teller/service/internal/processor/TellerTransactionProcessor.java
copy to service/src/main/java/io/mifos/teller/service/internal/processor/DepositTransactionHandler.java
index c03adff..ae78300 100644
--- a/service/src/main/java/io/mifos/teller/service/internal/processor/TellerTransactionProcessor.java
+++ b/service/src/main/java/io/mifos/teller/service/internal/processor/DepositTransactionHandler.java
@@ -41,7 +41,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 @Component
-public class TellerTransactionProcessor {
+public class DepositTransactionHandler {
 
   private final Logger logger;
   private final AccountingService accountingService;
@@ -49,10 +49,10 @@ public class TellerTransactionProcessor {
   private final TellerRepository tellerRepository;
 
   @Autowired
-  public TellerTransactionProcessor(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
-                                    final AccountingService accountingService,
-                                    final DepositAccountManagementService depositAccountManagementService,
-                                    final TellerRepository tellerRepository) {
+  public DepositTransactionHandler(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                                   final AccountingService accountingService,
+                                   final DepositAccountManagementService depositAccountManagementService,
+                                   final TellerRepository tellerRepository) {
     super();
     this.logger = logger;
     this.accountingService = accountingService;
@@ -60,42 +60,22 @@ public class TellerTransactionProcessor {
     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 getDepositTransactionCosts(final TellerTransaction tellerTransaction) {
+    final List<Charge> charges = this.depositAccountManagementService.getCharges(tellerTransaction);
 
-  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.getDepositTransactionCosts(tellerTransaction);
-      default:
-        throw new IllegalArgumentException("Unsupported TX type " + tellerTransaction.getTransactionType());
-    }
+    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) {
+  public void processTransfer(final String tellerCode, final TellerTransaction tellerTransaction,
+                               final boolean chargesIncluded) {
+    final TellerEntity tellerEntity = getTellerEntity(tellerCode);
     final JournalEntry journalEntry = this.prepareJournalEntry(tellerTransaction);
 
     final TellerTransactionCosts tellerTransactionCosts = this.getDepositTransactionCosts(tellerTransaction);
@@ -109,7 +89,11 @@ public class TellerTransactionProcessor {
     debtors.add(customerDebtor);
 
     if (!tellerTransactionCosts.getCharges().isEmpty()) {
-      debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
+      if (chargesIncluded) {
+        debtors.add(this.createChargesDebtor(tellerEntity.getTellerAccountIdentifier(), tellerTransactionCosts));
+      } else {
+        debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
+      }
     }
 
     final HashSet<Creditor> creditors = new HashSet<>();
@@ -125,14 +109,8 @@ public class TellerTransactionProcessor {
     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();
+  public void processCashDeposit(final String tellerCode, final TellerTransaction tellerTransaction, final boolean chargesIncluded) {
+    final TellerEntity tellerEntity = getTellerEntity(tellerCode);
     final JournalEntry journalEntry = this.prepareJournalEntry(tellerTransaction);
     final TellerTransactionCosts tellerTransactionCosts = this.getDepositTransactionCosts(tellerTransaction);
 
@@ -141,12 +119,15 @@ public class TellerTransactionProcessor {
 
     final Debtor tellerDebtor = new Debtor();
     tellerDebtor.setAccountNumber(tellerEntity.getTellerAccountIdentifier());
-    tellerDebtor.setAmount(tellerTransaction.getAmount().toString());
-    debtors.add(tellerDebtor);
-
-    if (!tellerTransactionCosts.getCharges().isEmpty()) {
-      debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
+    if (chargesIncluded) {
+      tellerDebtor.setAmount(tellerTransactionCosts.getTotalAmount().toString());
+    } else {
+      tellerDebtor.setAmount(tellerTransaction.getAmount().toString());
+      if (!tellerTransactionCosts.getCharges().isEmpty()) {
+        debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
+      }
     }
+    debtors.add(tellerDebtor);
 
     final HashSet<Creditor> creditors = new HashSet<>();
     journalEntry.setCreditors(creditors);
@@ -161,14 +142,9 @@ public class TellerTransactionProcessor {
     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();
+  public void processCashWithdrawal(final String tellerCode, final TellerTransaction tellerTransaction,
+                                    final boolean chargesIncluded) {
+    final TellerEntity tellerEntity = getTellerEntity(tellerCode);
     final JournalEntry journalEntry = this.prepareJournalEntry(tellerTransaction);
     final TellerTransactionCosts tellerTransactionCosts = this.getDepositTransactionCosts(tellerTransaction);
 
@@ -181,7 +157,11 @@ public class TellerTransactionProcessor {
     debtors.add(customerDebtor);
 
     if (!tellerTransactionCosts.getCharges().isEmpty()) {
-      debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
+      if (chargesIncluded) {
+        debtors.add(this.createChargesDebtor(tellerEntity.getTellerAccountIdentifier(), tellerTransactionCosts));
+      } else {
+        debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
+      }
     }
 
     final HashSet<Creditor> creditors = new HashSet<>();
@@ -197,28 +177,23 @@ public class TellerTransactionProcessor {
     this.accountingService.postJournalEntry(journalEntry);
   }
 
-  private void processDepositAccountClosing(final String tellerCode, final TellerTransaction tellerTransaction) {
-    final List<ProductInstance> productInstances =
-        this.depositAccountManagementService.fetchProductInstances(tellerTransaction.getCustomerIdentifier());
+  public void processDepositAccountClosing(final String tellerCode, final TellerTransaction tellerTransaction,
+                                            final boolean chargesIncluded) {
+    this.processCashWithdrawal(tellerCode, tellerTransaction, chargesIncluded);
 
-    this.processCashWithdrawal(tellerCode, tellerTransaction);
-
-    productInstances.forEach(productInstance -> {
-      if (productInstance.getAccountIdentifier().equals(tellerTransaction.getCustomerAccountIdentifier())) {
-        this.depositAccountManagementService.closeProductInstance(tellerTransaction.getCustomerAccountIdentifier());
-        this.accountingService.closeAccount(tellerTransaction.getCustomerAccountIdentifier());
-      }
-    });
+    this.depositAccountManagementService.closeProductInstance(tellerTransaction.getCustomerAccountIdentifier());
+    this.accountingService.closeAccount(tellerTransaction.getCustomerAccountIdentifier());
   }
 
-  private void processDepositAccountOpening(final String tellerCode, final TellerTransaction tellerTransaction) {
+  public void processDepositAccountOpening(final String tellerCode, final TellerTransaction tellerTransaction,
+                                            final boolean chargesIncluded) {
     final ProductInstance productInstances =
         this.depositAccountManagementService.findProductInstance(tellerTransaction.getCustomerAccountIdentifier());
 
     final ProductDefinition productDefinition =
         this.depositAccountManagementService.findProductDefinition(productInstances.getProductIdentifier());
 
-    this.processCashDeposit(tellerCode, tellerTransaction);
+    this.processCashDeposit(tellerCode, tellerTransaction, chargesIncluded);
 
     if ((tellerTransaction.getAmount() + productInstances.getBalance()) >= productDefinition.getMinimumBalance()) {
       this.depositAccountManagementService.activateProductInstance(tellerTransaction.getCustomerAccountIdentifier());
@@ -226,6 +201,16 @@ public class TellerTransactionProcessor {
     }
   }
 
+  private TellerEntity getTellerEntity(final String tellerCode) {
+    final Optional<TellerEntity> optionalTeller = this.tellerRepository.findByIdentifier(tellerCode);
+    if (!optionalTeller.isPresent()) {
+      this.logger.warn("Teller {} not found.", tellerCode);
+      throw new IllegalStateException("Teller not found.");
+    }
+
+    return optionalTeller.get();
+  }
+
   private JournalEntry prepareJournalEntry(final TellerTransaction tellerTransaction) {
     final JournalEntry journalEntry = new JournalEntry();
     journalEntry.setTransactionIdentifier(tellerTransaction.getIdentifier());
@@ -237,19 +222,6 @@ public class TellerTransactionProcessor {
     return journalEntry;
   }
 
-  private TellerTransactionCosts getDepositTransactionCosts(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 Debtor createChargesDebtor(final String accountIdentifier, final TellerTransactionCosts tellerTransactionCosts) {
     final Debtor chargesDebtor = new Debtor();
     chargesDebtor.setAccountNumber(accountIdentifier);
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
index c03adff..705911a 100644
--- 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
@@ -15,67 +15,44 @@
  */
 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.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 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;
+  private final DepositTransactionHandler depositTransactionHandler;
 
   @Autowired
   public TellerTransactionProcessor(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
-                                    final AccountingService accountingService,
-                                    final DepositAccountManagementService depositAccountManagementService,
-                                    final TellerRepository tellerRepository) {
+                                    final DepositTransactionHandler depositTransactionHandler) {
     super();
     this.logger = logger;
-    this.accountingService = accountingService;
-    this.depositAccountManagementService = depositAccountManagementService;
-    this.tellerRepository = tellerRepository;
+    this.depositTransactionHandler = depositTransactionHandler;
   }
 
-  public void process(final String tellerCode, final TellerTransaction tellerTransaction) {
+  public void process(final String tellerCode, final TellerTransaction tellerTransaction, final Boolean chargesIncluded) {
     switch (tellerTransaction.getTransactionType()) {
       case ServiceConstants.TX_OPEN_ACCOUNT:
-        this.processDepositAccountOpening(tellerCode, tellerTransaction);
+        this.depositTransactionHandler.processDepositAccountOpening(tellerCode, tellerTransaction, chargesIncluded);
         break;
       case ServiceConstants.TX_CLOSE_ACCOUNT:
-        this.processDepositAccountClosing(tellerCode, tellerTransaction);
+        this.depositTransactionHandler.processDepositAccountClosing(tellerCode, tellerTransaction, chargesIncluded);
         break;
       case ServiceConstants.TX_ACCOUNT_TRANSFER:
-        this.processTransfer(tellerTransaction);
+        this.depositTransactionHandler.processTransfer(tellerCode, tellerTransaction, chargesIncluded);
         break;
       case ServiceConstants.TX_CASH_DEPOSIT:
-        this.processCashDeposit(tellerCode, tellerTransaction);
+        this.depositTransactionHandler.processCashDeposit(tellerCode, tellerTransaction, chargesIncluded);
         break;
       case ServiceConstants.TX_CASH_WITHDRAWAL:
-        this.processCashWithdrawal(tellerCode, tellerTransaction);
+        this.depositTransactionHandler.processCashWithdrawal(tellerCode, tellerTransaction, chargesIncluded);
         break;
       default:
         throw new IllegalArgumentException("Unsupported TX type " + tellerTransaction.getTransactionType());
@@ -89,191 +66,10 @@ public class TellerTransactionProcessor {
       case ServiceConstants.TX_ACCOUNT_TRANSFER:
       case ServiceConstants.TX_CASH_DEPOSIT:
       case ServiceConstants.TX_CASH_WITHDRAWAL:
-        return this.getDepositTransactionCosts(tellerTransaction);
+        return this.depositTransactionHandler.getDepositTransactionCosts(tellerTransaction);
       default:
         throw new IllegalArgumentException("Unsupported TX type " + tellerTransaction.getTransactionType());
     }
   }
 
-  private void processTransfer(final TellerTransaction tellerTransaction) {
-    final JournalEntry journalEntry = this.prepareJournalEntry(tellerTransaction);
-
-    final TellerTransactionCosts tellerTransactionCosts = this.getDepositTransactionCosts(tellerTransaction);
-
-    final HashSet<Debtor> debtors = new HashSet<>();
-    journalEntry.setDebtors(debtors);
-
-    final Debtor customerDebtor = new Debtor();
-    customerDebtor.setAccountNumber(tellerTransaction.getCustomerAccountIdentifier());
-    customerDebtor.setAmount(tellerTransaction.getAmount().toString());
-    debtors.add(customerDebtor);
-
-    if (!tellerTransactionCosts.getCharges().isEmpty()) {
-      debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
-    }
-
-    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.getDepositTransactionCosts(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);
-
-    if (!tellerTransactionCosts.getCharges().isEmpty()) {
-      debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
-    }
-
-    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.getDepositTransactionCosts(tellerTransaction);
-
-    final HashSet<Debtor> debtors = new HashSet<>();
-    journalEntry.setDebtors(debtors);
-
-    final Debtor customerDebtor = new Debtor();
-    customerDebtor.setAccountNumber(tellerTransaction.getCustomerAccountIdentifier());
-    customerDebtor.setAmount(tellerTransaction.getAmount().toString());
-    debtors.add(customerDebtor);
-
-    if (!tellerTransactionCosts.getCharges().isEmpty()) {
-      debtors.add(this.createChargesDebtor(tellerTransaction.getCustomerAccountIdentifier(), tellerTransactionCosts));
-    }
-
-    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.fetchProductInstances(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 ProductInstance productInstances =
-        this.depositAccountManagementService.findProductInstance(tellerTransaction.getCustomerAccountIdentifier());
-
-    final ProductDefinition productDefinition =
-        this.depositAccountManagementService.findProductDefinition(productInstances.getProductIdentifier());
-
-    this.processCashDeposit(tellerCode, tellerTransaction);
-
-    if ((tellerTransaction.getAmount() + productInstances.getBalance()) >= productDefinition.getMinimumBalance()) {
-      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.setMessage(tellerTransaction.getTransactionType());
-    journalEntry.setClerk(UserContextHolder.checkedGetUser());
-
-    return journalEntry;
-  }
-
-  private TellerTransactionCosts getDepositTransactionCosts(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 Debtor createChargesDebtor(final String accountIdentifier, final TellerTransactionCosts tellerTransactionCosts) {
-    final Debtor chargesDebtor = new Debtor();
-    chargesDebtor.setAccountNumber(accountIdentifier);
-    chargesDebtor.setAmount(
-        Double.valueOf(
-            tellerTransactionCosts.getCharges()
-                .stream()
-                .mapToDouble(Charge::getAmount)
-                .sum()
-        ).toString()
-    );
-
-    return chargesDebtor;
-  }
-
-  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/service/src/main/java/io/mifos/teller/service/rest/TellerOperationRestController.java b/service/src/main/java/io/mifos/teller/service/rest/TellerOperationRestController.java
index 6f1104f..6295119 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
@@ -84,7 +84,7 @@ public class TellerOperationRestController {
   )
   @ResponseBody
   ResponseEntity<Teller> unlockDrawer(@PathVariable("tellerCode") final String tellerCode,
-                                    @RequestBody @Valid final UnlockDrawerCommand unlockDrawerCommand) {
+                                      @RequestBody @Valid final UnlockDrawerCommand unlockDrawerCommand) {
     final Teller teller = this.verifyTeller(tellerCode);
 
     if (teller.getState().equals(Teller.State.CLOSED.name())) {
@@ -170,11 +170,17 @@ public class TellerOperationRestController {
     if (!optionalCustomerAccount.isPresent()) {
       throw ServiceException.badRequest("Customer account {0} not found.");
     } else {
+      final Account customerAccount = optionalCustomerAccount.get();
+
+      if (!customerAccount.getState().equals(Account.State.OPEN)) {
+        throw ServiceException.conflict("Account {0} is not open.", customerAccount.getIdentifier());
+      }
+
       if (transactionType.equals(ServiceConstants.TX_ACCOUNT_TRANSFER)
           || transactionType.equals(ServiceConstants.TX_CASH_WITHDRAWAL)
           || transactionType.equals(ServiceConstants.TX_CLOSE_ACCOUNT)) {
 
-        final Account customerAccount = optionalCustomerAccount.get();
+
         if (customerAccount.getBalance() < tellerTransaction.getAmount()) {
           throw ServiceException.conflict("Not enough balance.");
         }
@@ -206,7 +212,8 @@ public class TellerOperationRestController {
   @ResponseBody
   ResponseEntity<Void> confirm(@PathVariable("tellerCode") final String tellerCode,
                                @PathVariable("identifier") final String tellerTransactionIdentifier,
-                               @RequestParam(value = "command", required = true) final String command) {
+                               @RequestParam(value = "command", required = true) final String command,
+                               @RequestParam(value = "charges", required = false, defaultValue = "excluded") final String charges) {
     final Teller teller = this.verifyTeller(tellerCode);
 
     this.verifyEmployee(teller);
@@ -217,7 +224,7 @@ public class TellerOperationRestController {
 
     switch (command.toUpperCase()) {
       case "CONFIRM" :
-        this.commandGateway.process(new ConfirmTellerTransactionCommand(tellerTransactionIdentifier));
+        this.commandGateway.process(new ConfirmTellerTransactionCommand(tellerTransactionIdentifier, charges));
         break;
       case "CANCEL" :
         this.commandGateway.process(new CancelTellerTransactionCommand(tellerTransactionIdentifier));

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