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 <[email protected]> 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' ]
