You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ju...@apache.org on 2019/10/09 07:39:17 UTC

[fineract-cn-cheques] 03/41: added MICR resolution for on us cheques

This is an automated email from the ASF dual-hosted git repository.

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

commit bad4be7333d76621370b669e3fd0261fe2ca7932
Author: mgeiss <mg...@mifos.org>
AuthorDate: Tue Aug 22 12:01:46 2017 +0200

    added MICR resolution for on us cheques
---
 .../mifos/cheque/api/v1/client/ChequeManager.java  |  19 ++
 .../client/DependingResourceNotValidException.java |  19 ++
 .../v1/client/InvalidChequeNumberException.java    |  19 ++
 .../api/v1/{client => domain}/IssuingCount.java    |   2 +-
 .../mifos/cheque/api/v1/domain/MICRResolution.java |  42 ++++
 .../src/main/java/io/mifos/cheque/TestCheques.java |   3 +-
 .../java/io/mifos/cheque/TestIssuingCheques.java   |   2 +-
 .../src/main/java/io/mifos/cheque/TestMICR.java    | 271 +++++++++++++++++++++
 service/build.gradle                               |   1 +
 .../mifos/cheque/service/ChequeConfiguration.java  |   4 +-
 .../service/internal/service/MICRService.java      |  91 +++++++
 .../internal/service/helper/AccountingService.java |  16 +-
 ...ganizationService.java => CustomerService.java} |  26 +-
 .../internal/service/helper/DepositService.java    |  10 +
 .../service/helper/OrganizationService.java        |  12 +-
 .../cheque/service/rest/ChequeRestController.java  |   2 +-
 .../cheque/service/rest/MIRCRestController.java    |  58 +++++
 shared.gradle                                      |   1 +
 18 files changed, 571 insertions(+), 27 deletions(-)

diff --git a/api/src/main/java/io/mifos/cheque/api/v1/client/ChequeManager.java b/api/src/main/java/io/mifos/cheque/api/v1/client/ChequeManager.java
index 781793a..466d20d 100644
--- a/api/src/main/java/io/mifos/cheque/api/v1/client/ChequeManager.java
+++ b/api/src/main/java/io/mifos/cheque/api/v1/client/ChequeManager.java
@@ -18,8 +18,14 @@ package io.mifos.cheque.api.v1.client;
 import io.mifos.cheque.api.v1.domain.Cheque;
 import io.mifos.cheque.api.v1.domain.ChequeProcessingCommand;
 import io.mifos.cheque.api.v1.domain.ChequeTransaction;
+import io.mifos.cheque.api.v1.domain.IssuingCount;
+import io.mifos.cheque.api.v1.domain.MICRResolution;
+import io.mifos.core.api.annotation.ThrowsException;
+import io.mifos.core.api.annotation.ThrowsExceptions;
 import io.mifos.core.api.util.CustomFeignClientsConfiguration;
+import io.mifos.core.api.util.NotFoundException;
 import org.springframework.cloud.netflix.feign.FeignClient;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -84,4 +90,17 @@ public interface ChequeManager {
       consumes = {MediaType.APPLICATION_JSON_VALUE}
   )
   void process(@RequestBody @Valid final ChequeTransaction chequeTransaction);
+
+  @RequestMapping(
+      value = "/micr/{identifier}",
+      method = RequestMethod.GET,
+      produces = {MediaType.ALL_VALUE},
+      consumes = {MediaType.APPLICATION_JSON_VALUE}
+  )
+  @ThrowsExceptions({
+      @ThrowsException(status = HttpStatus.NOT_FOUND, exception = NotFoundException.class),
+      @ThrowsException(status = HttpStatus.CONFLICT, exception = InvalidChequeNumberException.class),
+      @ThrowsException(status = HttpStatus.BAD_REQUEST, exception = DependingResourceNotValidException.class)
+  })
+  MICRResolution expandMicr(@PathVariable("identifier") final String identifier);
 }
diff --git a/api/src/main/java/io/mifos/cheque/api/v1/client/DependingResourceNotValidException.java b/api/src/main/java/io/mifos/cheque/api/v1/client/DependingResourceNotValidException.java
new file mode 100644
index 0000000..0c5d945
--- /dev/null
+++ b/api/src/main/java/io/mifos/cheque/api/v1/client/DependingResourceNotValidException.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Kuelap, Inc and its suppliers, if any.
+ * The intellectual and technical concepts contained herein
+ * are proprietary to Kuelap, Inc and its suppliers and may
+ * be covered by U.S. and Foreign Patents, patents in process,
+ * and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * Kuelap, Inc.
+ */
+package io.mifos.cheque.api.v1.client;
+
+public class DependingResourceNotValidException extends RuntimeException {
+}
diff --git a/api/src/main/java/io/mifos/cheque/api/v1/client/InvalidChequeNumberException.java b/api/src/main/java/io/mifos/cheque/api/v1/client/InvalidChequeNumberException.java
new file mode 100644
index 0000000..29084cf
--- /dev/null
+++ b/api/src/main/java/io/mifos/cheque/api/v1/client/InvalidChequeNumberException.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Kuelap, Inc and its suppliers, if any.
+ * The intellectual and technical concepts contained herein
+ * are proprietary to Kuelap, Inc and its suppliers and may
+ * be covered by U.S. and Foreign Patents, patents in process,
+ * and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * Kuelap, Inc.
+ */
+package io.mifos.cheque.api.v1.client;
+
+public class InvalidChequeNumberException extends RuntimeException {
+}
diff --git a/api/src/main/java/io/mifos/cheque/api/v1/client/IssuingCount.java b/api/src/main/java/io/mifos/cheque/api/v1/domain/IssuingCount.java
similarity index 97%
rename from api/src/main/java/io/mifos/cheque/api/v1/client/IssuingCount.java
rename to api/src/main/java/io/mifos/cheque/api/v1/domain/IssuingCount.java
index af37b0d..70a4f53 100644
--- a/api/src/main/java/io/mifos/cheque/api/v1/client/IssuingCount.java
+++ b/api/src/main/java/io/mifos/cheque/api/v1/domain/IssuingCount.java
@@ -13,7 +13,7 @@
  * is strictly forbidden unless prior written permission is obtained
  * Kuelap, Inc.
  */
-package io.mifos.cheque.api.v1.client;
+package io.mifos.cheque.api.v1.domain;
 
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
diff --git a/api/src/main/java/io/mifos/cheque/api/v1/domain/MICRResolution.java b/api/src/main/java/io/mifos/cheque/api/v1/domain/MICRResolution.java
new file mode 100644
index 0000000..21c2aae
--- /dev/null
+++ b/api/src/main/java/io/mifos/cheque/api/v1/domain/MICRResolution.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Kuelap, Inc and its suppliers, if any.
+ * The intellectual and technical concepts contained herein
+ * are proprietary to Kuelap, Inc and its suppliers and may
+ * be covered by U.S. and Foreign Patents, patents in process,
+ * and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * Kuelap, Inc.
+ */
+package io.mifos.cheque.api.v1.domain;
+
+public class MICRResolution {
+
+  private String office;
+  private String customer;
+
+  public MICRResolution() {
+    super();
+  }
+
+  public String getOffice() {
+    return this.office;
+  }
+
+  public void setOffice(final String office) {
+    this.office = office;
+  }
+
+  public String getCustomer() {
+    return this.customer;
+  }
+
+  public void setCustomer(final String customer) {
+    this.customer = customer;
+  }
+}
diff --git a/component-test/src/main/java/io/mifos/cheque/TestCheques.java b/component-test/src/main/java/io/mifos/cheque/TestCheques.java
index 78fd545..5ed5c14 100644
--- a/component-test/src/main/java/io/mifos/cheque/TestCheques.java
+++ b/component-test/src/main/java/io/mifos/cheque/TestCheques.java
@@ -20,11 +20,11 @@ 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.cheque.api.v1.EventConstants;
-import io.mifos.cheque.api.v1.client.IssuingCount;
 import io.mifos.cheque.api.v1.domain.Action;
 import io.mifos.cheque.api.v1.domain.Cheque;
 import io.mifos.cheque.api.v1.domain.ChequeProcessingCommand;
 import io.mifos.cheque.api.v1.domain.ChequeTransaction;
+import io.mifos.cheque.api.v1.domain.IssuingCount;
 import io.mifos.cheque.api.v1.domain.State;
 import io.mifos.cheque.service.internal.format.MICRParser;
 import io.mifos.cheque.service.internal.service.helper.AccountingService;
@@ -81,7 +81,6 @@ public class TestCheques extends AbstractChequeTest {
         .doAnswer(invocation -> true)
         .when(this.accountingServiceSpy).accountExists(randomCheque.getMicr().getAccountNumber());
 
-
     final ChequeTransaction chequeTransaction = new ChequeTransaction();
     chequeTransaction.setCheque(randomCheque);
     chequeTransaction.setCreditorAccountNumber(RandomStringUtils.randomAlphanumeric(34));
diff --git a/component-test/src/main/java/io/mifos/cheque/TestIssuingCheques.java b/component-test/src/main/java/io/mifos/cheque/TestIssuingCheques.java
index 474ad4b..364636d 100644
--- a/component-test/src/main/java/io/mifos/cheque/TestIssuingCheques.java
+++ b/component-test/src/main/java/io/mifos/cheque/TestIssuingCheques.java
@@ -15,7 +15,7 @@
  */
 package io.mifos.cheque;
 
-import io.mifos.cheque.api.v1.client.IssuingCount;
+import io.mifos.cheque.api.v1.domain.IssuingCount;
 import io.mifos.cheque.service.internal.repository.IssuedChequeEntity;
 import io.mifos.cheque.service.internal.repository.IssuedChequeRepository;
 import io.mifos.cheque.service.internal.service.helper.AccountingService;
diff --git a/component-test/src/main/java/io/mifos/cheque/TestMICR.java b/component-test/src/main/java/io/mifos/cheque/TestMICR.java
new file mode 100644
index 0000000..cc50bae
--- /dev/null
+++ b/component-test/src/main/java/io/mifos/cheque/TestMICR.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Kuelap, Inc and its suppliers, if any.
+ * The intellectual and technical concepts contained herein
+ * are proprietary to Kuelap, Inc and its suppliers and may
+ * be covered by U.S. and Foreign Patents, patents in process,
+ * and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * Kuelap, Inc.
+ */
+package io.mifos.cheque;
+
+import io.mifos.cheque.api.v1.EventConstants;
+import io.mifos.cheque.api.v1.client.DependingResourceNotValidException;
+import io.mifos.cheque.api.v1.client.InvalidChequeNumberException;
+import io.mifos.cheque.api.v1.domain.Cheque;
+import io.mifos.cheque.api.v1.domain.ChequeTransaction;
+import io.mifos.cheque.api.v1.domain.IssuingCount;
+import io.mifos.cheque.api.v1.domain.MICR;
+import io.mifos.cheque.api.v1.domain.MICRResolution;
+import io.mifos.cheque.service.internal.format.MICRParser;
+import io.mifos.cheque.service.internal.service.helper.AccountingService;
+import io.mifos.cheque.service.internal.service.helper.CustomerService;
+import io.mifos.cheque.service.internal.service.helper.DepositService;
+import io.mifos.cheque.service.internal.service.helper.OrganizationService;
+import io.mifos.core.api.util.NotFoundException;
+import io.mifos.customer.api.v1.domain.Customer;
+import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
+import io.mifos.office.api.v1.domain.Office;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import java.util.Collections;
+import java.util.Optional;
+
+public class TestMICR extends AbstractChequeTest {
+
+  private static final String OFFICE_NAME = "Money Bin";
+  private static final String CUSTOMER_IDENTIFIER = "scrooge.mcduck";
+  private static final String GIVEN_NAME = "Scrooge";
+  private static final String SURNAME = "Mc Duck";
+
+  @MockBean
+  private OrganizationService organizationServiceSpy;
+
+  @MockBean
+  private DepositService depositServiceSpy;
+
+  @MockBean
+  private AccountingService accountingServiceSpy;
+
+  @MockBean
+  private CustomerService customerServiceSpy;
+
+  @Autowired
+  public TestMICR() {
+    super();
+  }
+
+  @Test
+  public void shouldReturnResolution() throws Exception {
+
+    final MICR micr = Fixture.createRandomCheque().getMicr();
+
+    Mockito
+        .doAnswer(invocation -> {
+          final Office mockedOffice = new Office();
+          mockedOffice.setName(TestMICR.OFFICE_NAME);
+          return Optional.of(mockedOffice);
+        })
+        .when(this.organizationServiceSpy).findOffice(micr.getBranchSortCode());
+
+    Mockito
+        .doAnswer(invocation -> {
+          final ProductInstance mockedProductInstance = new ProductInstance();
+          mockedProductInstance.setCustomerIdentifier(TestMICR.CUSTOMER_IDENTIFIER);
+          return Optional.of(mockedProductInstance);
+        })
+        .when(this.depositServiceSpy).findProductInstance(micr.getAccountNumber());
+
+    Mockito
+        .doAnswer(invocation -> Collections.emptyList())
+        .when(this.depositServiceSpy).getIssueChequeCharges(micr.getAccountNumber());
+
+    Mockito
+        .doAnswer(invocation -> {
+          final Customer mockedCustomer = new Customer();
+          mockedCustomer.setGivenName(TestMICR.GIVEN_NAME);
+          mockedCustomer.setSurname(TestMICR.SURNAME);
+          return Optional.of(mockedCustomer);
+        })
+        .when(this.customerServiceSpy).findCustomer(TestMICR.CUSTOMER_IDENTIFIER);
+
+    final IssuingCount issuingCount = new IssuingCount();
+    issuingCount.setAccountIdentifier(micr.getAccountNumber());
+    issuingCount.setAmount(100);
+
+    super.chequeManager.issue(issuingCount);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.ISSUE_CHEQUES, micr.getAccountNumber())
+    );
+
+    final MICRResolution micrResolution = super.chequeManager.expandMicr(MICRParser.toIdentifier(micr));
+    Assert.assertNotNull(micrResolution);
+    Assert.assertEquals(OFFICE_NAME, micrResolution.getOffice());
+    Assert.assertEquals(GIVEN_NAME + " " + SURNAME, micrResolution.getCustomer());
+  }
+
+  @Test(expected = NotFoundException.class)
+  public void shouldNotReturnResolutionNotOnUs() throws Exception {
+    final MICR micr = Fixture.createRandomCheque().getMicr();
+
+    Mockito
+        .doAnswer(invocation -> Optional.empty())
+        .when(this.organizationServiceSpy).findOffice(micr.getBranchSortCode());
+
+    super.chequeManager.expandMicr(MICRParser.toIdentifier(micr));
+  }
+
+  @Test(expected = InvalidChequeNumberException.class)
+  public void shouldNotReturnResolutionChequeAlreadyUsed() throws Exception {
+    final Cheque randomCheque = Fixture.createRandomCheque();
+    final MICR micr = randomCheque.getMicr();
+
+    final IssuingCount issuingCount = new IssuingCount();
+    issuingCount.setAccountIdentifier(micr.getAccountNumber());
+    issuingCount.setAmount(100);
+
+    super.chequeManager.issue(issuingCount);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.ISSUE_CHEQUES, micr.getAccountNumber())
+    );
+
+    Mockito
+        .doAnswer(invocation -> true)
+        .when(this.organizationServiceSpy).officeExistsByBranchSortCode(randomCheque.getMicr().getBranchSortCode());
+
+    Mockito
+        .doAnswer(invocation -> true)
+        .when(this.accountingServiceSpy).accountExists(randomCheque.getMicr().getAccountNumber());
+
+    final ChequeTransaction chequeTransaction = new ChequeTransaction();
+    chequeTransaction.setCheque(randomCheque);
+    chequeTransaction.setCreditorAccountNumber(RandomStringUtils.randomAlphanumeric(34));
+    super.chequeManager.process(chequeTransaction);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.CHEQUE_TRANSACTION, MICRParser.toIdentifier(randomCheque.getMicr()))
+    );
+
+    super.chequeManager.expandMicr(MICRParser.toIdentifier(micr));
+  }
+
+  @Test(expected = InvalidChequeNumberException.class)
+  public void shouldNotReturnResolutionChequeNotIssued() throws Exception {
+    final MICR micr = Fixture.createRandomCheque().getMicr();
+
+    Mockito
+        .doAnswer(invocation -> {
+          final Office mockedOffice = new Office();
+          mockedOffice.setName(TestMICR.OFFICE_NAME);
+          return Optional.of(mockedOffice);
+        })
+        .when(this.organizationServiceSpy).findOffice(micr.getBranchSortCode());
+
+    super.chequeManager.expandMicr(MICRParser.toIdentifier(micr));
+  }
+
+  @Test(expected = InvalidChequeNumberException.class)
+  public void shouldNotReturnResolutionUnknownChequeNumber() throws Exception {
+    final MICR micr = Fixture.createRandomCheque().getMicr();
+    micr.setChequeNumber("0815");
+
+    final IssuingCount issuingCount = new IssuingCount();
+    issuingCount.setAccountIdentifier(micr.getAccountNumber());
+    issuingCount.setAmount(100);
+
+    super.chequeManager.issue(issuingCount);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.ISSUE_CHEQUES, micr.getAccountNumber())
+    );
+
+    Mockito
+        .doAnswer(invocation -> {
+          final Office mockedOffice = new Office();
+          mockedOffice.setName(TestMICR.OFFICE_NAME);
+          return Optional.of(mockedOffice);
+        })
+        .when(this.organizationServiceSpy).findOffice(micr.getBranchSortCode());
+
+    super.chequeManager.expandMicr(MICRParser.toIdentifier(micr));
+  }
+
+  @Test(expected = DependingResourceNotValidException.class)
+  public void shouldNotReturnResolutionUnknownAccount() throws Exception {
+    final MICR micr = Fixture.createRandomCheque().getMicr();
+
+    Mockito
+        .doAnswer(invocation -> {
+          final Office mockedOffice = new Office();
+          mockedOffice.setName(TestMICR.OFFICE_NAME);
+          return Optional.of(mockedOffice);
+        })
+        .when(this.organizationServiceSpy).findOffice(micr.getBranchSortCode());
+
+    Mockito
+        .doAnswer(invocation -> Optional.empty())
+        .when(this.depositServiceSpy).findProductInstance(micr.getAccountNumber());
+
+    final IssuingCount issuingCount = new IssuingCount();
+    issuingCount.setAccountIdentifier(micr.getAccountNumber());
+    issuingCount.setAmount(100);
+
+    super.chequeManager.issue(issuingCount);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.ISSUE_CHEQUES, micr.getAccountNumber())
+    );
+
+    super.chequeManager.expandMicr(MICRParser.toIdentifier(micr));
+  }
+
+  @Test(expected = DependingResourceNotValidException.class)
+  public void shouldNotReturnResolutionUnknownCustomer() throws Exception {
+    final MICR micr = Fixture.createRandomCheque().getMicr();
+
+    Mockito
+        .doAnswer(invocation -> {
+          final Office mockedOffice = new Office();
+          mockedOffice.setName(TestMICR.OFFICE_NAME);
+          return Optional.of(mockedOffice);
+        })
+        .when(this.organizationServiceSpy).findOffice(micr.getBranchSortCode());
+
+    Mockito
+        .doAnswer(invocation -> {
+          final ProductInstance mockedProductInstance = new ProductInstance();
+          mockedProductInstance.setCustomerIdentifier(TestMICR.CUSTOMER_IDENTIFIER);
+          return Optional.of(mockedProductInstance);
+        })
+        .when(this.depositServiceSpy).findProductInstance(micr.getAccountNumber());
+
+    Mockito
+        .doAnswer(invocation -> Optional.empty())
+        .when(this.customerServiceSpy).findCustomer(TestMICR.CUSTOMER_IDENTIFIER);
+
+    final IssuingCount issuingCount = new IssuingCount();
+    issuingCount.setAccountIdentifier(micr.getAccountNumber());
+    issuingCount.setAmount(100);
+
+    super.chequeManager.issue(issuingCount);
+
+    Assert.assertTrue(
+        super.eventRecorder.wait(EventConstants.ISSUE_CHEQUES, micr.getAccountNumber())
+    );
+
+    super.chequeManager.expandMicr(MICRParser.toIdentifier(micr));
+  }
+}
diff --git a/service/build.gradle b/service/build.gradle
index af26fc0..234e970 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -35,6 +35,7 @@ dependencies {
             [group: 'io.mifos.accounting', name: 'api', version: versions.frameworkaccounting],
             [group: 'io.mifos.deposit-account-management', name: 'api', version: versions.frameworkdeposit],
             [group: 'io.mifos.office', name: 'api', version: versions.frameworkoffice],
+            [group: 'io.mifos.customer', name: 'api', version: versions.frameworkcustomer],
             [group: 'com.google.code.gson', name: 'gson'],
             [group: 'io.mifos.core', name: 'lang', version: versions.frameworklang],
             [group: 'io.mifos.core', name: 'async', version: versions.frameworkasync],
diff --git a/service/src/main/java/io/mifos/cheque/service/ChequeConfiguration.java b/service/src/main/java/io/mifos/cheque/service/ChequeConfiguration.java
index f1f0570..9e9919c 100644
--- a/service/src/main/java/io/mifos/cheque/service/ChequeConfiguration.java
+++ b/service/src/main/java/io/mifos/cheque/service/ChequeConfiguration.java
@@ -23,6 +23,7 @@ import io.mifos.core.command.config.EnableCommandProcessing;
 import io.mifos.core.lang.config.EnableServiceException;
 import io.mifos.core.lang.config.EnableTenantContext;
 import io.mifos.core.mariadb.config.EnableMariaDB;
+import io.mifos.customer.api.v1.client.CustomerManager;
 import io.mifos.deposit.api.v1.client.DepositAccountManager;
 import io.mifos.office.api.v1.client.OrganizationManager;
 import org.slf4j.Logger;
@@ -52,7 +53,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
     clients = {
         LedgerManager.class,
         DepositAccountManager.class,
-        OrganizationManager.class
+        OrganizationManager.class,
+        CustomerManager.class
     }
 )
 @ComponentScan({
diff --git a/service/src/main/java/io/mifos/cheque/service/internal/service/MICRService.java b/service/src/main/java/io/mifos/cheque/service/internal/service/MICRService.java
new file mode 100644
index 0000000..4bd3351
--- /dev/null
+++ b/service/src/main/java/io/mifos/cheque/service/internal/service/MICRService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Kuelap, Inc and its suppliers, if any.
+ * The intellectual and technical concepts contained herein
+ * are proprietary to Kuelap, Inc and its suppliers and may
+ * be covered by U.S. and Foreign Patents, patents in process,
+ * and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * Kuelap, Inc.
+ */
+package io.mifos.cheque.service.internal.service;
+
+import io.mifos.cheque.api.v1.domain.MICR;
+import io.mifos.cheque.api.v1.domain.MICRResolution;
+import io.mifos.cheque.service.ServiceConstants;
+import io.mifos.cheque.service.internal.repository.IssuedChequeEntity;
+import io.mifos.cheque.service.internal.repository.IssuedChequeRepository;
+import io.mifos.cheque.service.internal.service.helper.CustomerService;
+import io.mifos.cheque.service.internal.service.helper.DepositService;
+import io.mifos.cheque.service.internal.service.helper.OrganizationService;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.customer.api.v1.domain.Customer;
+import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
+import io.mifos.office.api.v1.domain.Office;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MICRService {
+
+  private final Logger logger;
+  private final ChequeService chequeService;
+  private final OrganizationService organizationService;
+  private final DepositService depositService;
+  private final CustomerService customerService;
+  private final IssuedChequeRepository issuedChequeRepository;
+
+  @Autowired
+  public MICRService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                     final ChequeService chequeService,
+                     final OrganizationService organizationService,
+                     final DepositService depositService,
+                     final CustomerService customerService,
+                     final IssuedChequeRepository issuedChequeRepository) {
+    super();
+    this.logger = logger;
+    this.chequeService = chequeService;
+    this.organizationService = organizationService;
+    this.depositService = depositService;
+    this.customerService = customerService;
+    this.issuedChequeRepository = issuedChequeRepository;
+  }
+
+  public MICRResolution expand(final MICR micr) {
+
+    if (this.chequeService.findBy(micr).isPresent()) {
+      throw ServiceException.conflict("Cheque already used.");
+    }
+
+    final Office office =
+        this.organizationService.findOffice(micr.getBranchSortCode())
+            .orElseThrow(() -> ServiceException.notFound("Given MICR is unknown."));
+
+    final IssuedChequeEntity issuedChequeEntity =
+        this.issuedChequeRepository.findByAccountIdentifier(micr.getAccountNumber())
+            .orElseThrow(() -> ServiceException.conflict("Cheque was never issued."));
+    if (Integer.valueOf(micr.getChequeNumber()) > issuedChequeEntity.getLastIssuedNumber()) {
+      throw ServiceException.conflict("Cheque number invalid.");
+    }
+
+    final ProductInstance productInstance =
+        this.depositService.findProductInstance(micr.getAccountNumber())
+            .orElseThrow(() -> ServiceException.badRequest("Given account not valid."));
+
+    final Customer customer =
+        this.customerService.findCustomer(productInstance.getCustomerIdentifier())
+            .orElseThrow(() -> ServiceException.badRequest("Given customer not valid."));
+
+    final MICRResolution micrResolution = new MICRResolution();
+    micrResolution.setOffice(office.getName());
+    micrResolution.setCustomer(customer.getGivenName() + " " + customer.getSurname());
+    return micrResolution;
+  }
+}
diff --git a/service/src/main/java/io/mifos/cheque/service/internal/service/helper/AccountingService.java b/service/src/main/java/io/mifos/cheque/service/internal/service/helper/AccountingService.java
index 95ef589..4667c04 100644
--- a/service/src/main/java/io/mifos/cheque/service/internal/service/helper/AccountingService.java
+++ b/service/src/main/java/io/mifos/cheque/service/internal/service/helper/AccountingService.java
@@ -35,6 +35,7 @@ import org.springframework.stereotype.Service;
 import java.time.Clock;
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Optional;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
@@ -99,12 +100,7 @@ public class AccountingService {
   }
 
   public boolean accountExists(final String accountIdentifier) {
-    try {
-      this.ledgerManager.findAccount(accountIdentifier);
-      return true;
-    } catch (final AccountNotFoundException anfex) {
-      return false;
-    }
+    return this.findAccount(accountIdentifier).isPresent();
   }
 
   public JournalEntry findJournalEntry(final String identifier) {
@@ -114,4 +110,12 @@ public class AccountingService {
   public void processJournalEntry(final JournalEntry journalEntry) {
     this.ledgerManager.createJournalEntry(journalEntry);
   }
+
+  public Optional<Account> findAccount(final String accountNumber) {
+    try {
+      return Optional.of(this.ledgerManager.findAccount(accountNumber));
+    } catch (final AccountNotFoundException anfex) {
+      return Optional.empty();
+    }
+  }
 }
diff --git a/service/src/main/java/io/mifos/cheque/service/internal/service/helper/OrganizationService.java b/service/src/main/java/io/mifos/cheque/service/internal/service/helper/CustomerService.java
similarity index 58%
copy from service/src/main/java/io/mifos/cheque/service/internal/service/helper/OrganizationService.java
copy to service/src/main/java/io/mifos/cheque/service/internal/service/helper/CustomerService.java
index f8e857a..3bfebcd 100644
--- a/service/src/main/java/io/mifos/cheque/service/internal/service/helper/OrganizationService.java
+++ b/service/src/main/java/io/mifos/cheque/service/internal/service/helper/CustomerService.java
@@ -16,33 +16,35 @@
 package io.mifos.cheque.service.internal.service.helper;
 
 import io.mifos.cheque.service.ServiceConstants;
-import io.mifos.office.api.v1.client.NotFoundException;
-import io.mifos.office.api.v1.client.OrganizationManager;
+import io.mifos.customer.api.v1.client.CustomerManager;
+import io.mifos.customer.api.v1.client.CustomerNotFoundException;
+import io.mifos.customer.api.v1.domain.Customer;
 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.Optional;
+
 @Service
-public class OrganizationService {
+public class CustomerService {
 
   private final Logger logger;
-  private final OrganizationManager organizationManager;
+  private final CustomerManager customerManager;
 
   @Autowired
-  public OrganizationService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
-                             final OrganizationManager organizationManager) {
+  public CustomerService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                         final CustomerManager customerManager) {
     super();
     this.logger = logger;
-    this.organizationManager = organizationManager;
+    this.customerManager = customerManager;
   }
 
-  public boolean officeExistsByBranchSortCode(final String branchSortCode) {
+  public Optional<Customer> findCustomer(final String identifier) {
     try {
-      this.organizationManager.findOfficeByIdentifier(branchSortCode);
-      return true;
-    } catch (final NotFoundException nfex) {
-      return false;
+      return Optional.of(this.customerManager.findCustomer(identifier));
+    } catch (final CustomerNotFoundException cnfex) {
+      return Optional.empty();
     }
   }
 }
diff --git a/service/src/main/java/io/mifos/cheque/service/internal/service/helper/DepositService.java b/service/src/main/java/io/mifos/cheque/service/internal/service/helper/DepositService.java
index 7047bf8..0ff8257 100644
--- a/service/src/main/java/io/mifos/cheque/service/internal/service/helper/DepositService.java
+++ b/service/src/main/java/io/mifos/cheque/service/internal/service/helper/DepositService.java
@@ -20,6 +20,7 @@ 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.Charge;
 import io.mifos.deposit.api.v1.definition.domain.ProductDefinition;
+import io.mifos.deposit.api.v1.instance.ProductInstanceNotFoundException;
 import io.mifos.deposit.api.v1.instance.domain.ProductInstance;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +28,7 @@ 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
@@ -75,4 +77,12 @@ public class DepositService {
 
     return productDefinition.getCashAccountIdentifier();
   }
+
+  public Optional<ProductInstance> findProductInstance(final String accountNumber) {
+    try {
+      return Optional.of(this.depositAccountManager.findProductInstance(accountNumber));
+    } catch (final ProductInstanceNotFoundException pinfex) {
+      return Optional.empty();
+    }
+  }
 }
diff --git a/service/src/main/java/io/mifos/cheque/service/internal/service/helper/OrganizationService.java b/service/src/main/java/io/mifos/cheque/service/internal/service/helper/OrganizationService.java
index f8e857a..4a11088 100644
--- a/service/src/main/java/io/mifos/cheque/service/internal/service/helper/OrganizationService.java
+++ b/service/src/main/java/io/mifos/cheque/service/internal/service/helper/OrganizationService.java
@@ -18,11 +18,14 @@ package io.mifos.cheque.service.internal.service.helper;
 import io.mifos.cheque.service.ServiceConstants;
 import io.mifos.office.api.v1.client.NotFoundException;
 import io.mifos.office.api.v1.client.OrganizationManager;
+import io.mifos.office.api.v1.domain.Office;
 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.Optional;
+
 @Service
 public class OrganizationService {
 
@@ -38,11 +41,14 @@ public class OrganizationService {
   }
 
   public boolean officeExistsByBranchSortCode(final String branchSortCode) {
+    return this.findOffice(branchSortCode).isPresent();
+  }
+
+  public Optional<Office> findOffice(final String branchSortCode) {
     try {
-      this.organizationManager.findOfficeByIdentifier(branchSortCode);
-      return true;
+      return Optional.of(this.organizationManager.findOfficeByIdentifier(branchSortCode));
     } catch (final NotFoundException nfex) {
-      return false;
+      return Optional.empty();
     }
   }
 }
diff --git a/service/src/main/java/io/mifos/cheque/service/rest/ChequeRestController.java b/service/src/main/java/io/mifos/cheque/service/rest/ChequeRestController.java
index 7f9414f..007e383 100644
--- a/service/src/main/java/io/mifos/cheque/service/rest/ChequeRestController.java
+++ b/service/src/main/java/io/mifos/cheque/service/rest/ChequeRestController.java
@@ -18,10 +18,10 @@ package io.mifos.cheque.service.rest;
 import io.mifos.anubis.annotation.AcceptedTokenType;
 import io.mifos.anubis.annotation.Permittable;
 import io.mifos.cheque.api.v1.PermittableGroupIds;
-import io.mifos.cheque.api.v1.client.IssuingCount;
 import io.mifos.cheque.api.v1.domain.Action;
 import io.mifos.cheque.api.v1.domain.Cheque;
 import io.mifos.cheque.api.v1.domain.ChequeProcessingCommand;
+import io.mifos.cheque.api.v1.domain.IssuingCount;
 import io.mifos.cheque.service.ServiceConstants;
 import io.mifos.cheque.service.internal.command.ApproveChequeTransactionCommand;
 import io.mifos.cheque.service.internal.command.CancelChequeTransactionCommand;
diff --git a/service/src/main/java/io/mifos/cheque/service/rest/MIRCRestController.java b/service/src/main/java/io/mifos/cheque/service/rest/MIRCRestController.java
new file mode 100644
index 0000000..8b56e73
--- /dev/null
+++ b/service/src/main/java/io/mifos/cheque/service/rest/MIRCRestController.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Kuelap, Inc and its suppliers, if any.
+ * The intellectual and technical concepts contained herein
+ * are proprietary to Kuelap, Inc and its suppliers and may
+ * be covered by U.S. and Foreign Patents, patents in process,
+ * and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * Kuelap, Inc.
+ */
+package io.mifos.cheque.service.rest;
+
+import io.mifos.cheque.api.v1.domain.MICRResolution;
+import io.mifos.cheque.service.ServiceConstants;
+import io.mifos.cheque.service.internal.format.MICRParser;
+import io.mifos.cheque.service.internal.service.MICRService;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/micr")
+public class MIRCRestController {
+
+  private final Logger logger;
+  private final MICRService micrService;
+
+  @Autowired
+  public MIRCRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                            final MICRService micrService) {
+    super();
+    this.logger = logger;
+    this.micrService = micrService;
+  }
+
+  @RequestMapping(
+      value = "/{identifier}",
+      method = RequestMethod.GET,
+      produces = {MediaType.APPLICATION_JSON_VALUE},
+      consumes = {MediaType.ALL_VALUE}
+  )
+  @ResponseBody
+  ResponseEntity<MICRResolution> expandMicr(@PathVariable("identifier") final String identifier) {
+    return ResponseEntity.ok(this.micrService.expand(MICRParser.fromIdentifier(identifier)));
+  }
+}
diff --git a/shared.gradle b/shared.gradle
index 53a6aa4..bddedf5 100644
--- a/shared.gradle
+++ b/shared.gradle
@@ -13,6 +13,7 @@ ext.versions = [
         frameworkoffice    : '0.1.0-BUILD-SNAPSHOT',
         frameworkaccounting: '0.1.0-BUILD-SNAPSHOT',
         frameworkdeposit   : '0.1.0-BUILD-SNAPSHOT',
+        frameworkcustomer  : '0.1.0-BUILD-SNAPSHOT',
         validator          : '5.3.0.Final'
 ]