Checking for invalid customer.
diff --git a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
index adadf68..4c7dfcd 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
@@ -21,6 +21,7 @@
import io.mifos.core.test.fixture.TenantDataStoreContextTestRule;
import io.mifos.core.test.listener.EnableEventRecording;
import io.mifos.core.test.listener.EventRecorder;
+import io.mifos.customer.api.v1.client.CustomerManager;
import io.mifos.individuallending.api.v1.client.IndividualLending;
import io.mifos.individuallending.api.v1.domain.product.AccountDesignators;
import io.mifos.individuallending.api.v1.domain.workflow.Action;
@@ -32,6 +33,7 @@
import io.mifos.portfolio.service.internal.util.RhythmAdapter;
import org.junit.*;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -114,6 +116,9 @@
@MockBean
LedgerManager ledgerManager;
+ @MockBean
+ CustomerManager customerManager;
+
@SuppressWarnings("SpringAutowiredFieldsWarningInspection")
@Autowired
@Qualifier(LOGGER_NAME)
@@ -123,6 +128,7 @@
public void prepTest() {
userContext = this.tenantApplicationSecurityEnvironment.createAutoUserContext(TEST_USER);
AccountingFixture.mockAccountingPrereqs(ledgerManager);
+ Mockito.doReturn(true).when(customerManager).isCustomerInGoodStanding(Fixture.CUSTOMER_IDENTIFIER);
}
@After
diff --git a/component-test/src/main/java/io/mifos/portfolio/Fixture.java b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
index 5b3785e..0f02397 100644
--- a/component-test/src/main/java/io/mifos/portfolio/Fixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
@@ -41,6 +41,7 @@
static final int MINOR_CURRENCY_UNIT_DIGITS = 2;
static final BigDecimal INTEREST_RATE = BigDecimal.valueOf(0.10).setScale(4, RoundingMode.HALF_EVEN);
static final BigDecimal ACCRUAL_PERIODS = BigDecimal.valueOf(365.2425);
+ public static final String CUSTOMER_IDENTIFIER = "alice";
private static int uniquenessSuffix = 0;
@@ -134,7 +135,7 @@
{
final CaseParameters ret = new CaseParameters(generateUniqueIdentifer("fred"));
- ret.setCustomerIdentifier("alice");
+ ret.setCustomerIdentifier(CUSTOMER_IDENTIFIER);
ret.setMaximumBalance(fixScale(BigDecimal.valueOf(2000L)));
ret.setTermRange(new TermRange(ChronoUnit.MONTHS, 18));
ret.setPaymentCycle(new PaymentCycle(ChronoUnit.MONTHS, 1, 1, null, null));
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestCases.java b/component-test/src/main/java/io/mifos/portfolio/TestCases.java
index 7493e81..c7f730e 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestCases.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestCases.java
@@ -29,6 +29,7 @@
import io.mifos.portfolio.api.v1.events.EventConstants;
import org.junit.Assert;
import org.junit.Test;
+import org.mockito.Mockito;
import java.math.BigDecimal;
import java.util.*;
@@ -199,6 +200,23 @@
}
@Test
+ public void shouldThrowWhenCustomerNotInGoodStanding() throws InterruptedException {
+ Mockito.doReturn(false).when(customerManager).isCustomerInGoodStanding("don");
+
+ final Product product = createProduct();
+
+ final CaseParameters newCaseParameters = Fixture.createAdjustedCaseParameters(x -> x.setCustomerIdentifier("don"));
+ final String originalParameters = new Gson().toJson(newCaseParameters);
+
+ try {
+ createAdjustedCase(product.getIdentifier(), x -> x.setParameters(originalParameters));
+ Assert.fail("This should cause an illegal argument exception because Don is not a customer in good standing.");
+ }
+ catch (final IllegalArgumentException ignored){
+ }
+ }
+
+ @Test
public void shouldThrowWhenProductNotActivated() throws InterruptedException {
final Product product = createProduct();
diff --git a/service/build.gradle b/service/build.gradle
index f4f79d6..8194a22 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -36,6 +36,7 @@
[group: 'io.mifos.rhythm', name: 'api', version: versions.mifosrhythm],
[group: 'io.mifos.accounting', name: 'api', version: versions.frameworkaccounting],
[group: 'io.mifos.anubis', name: 'library', version: versions.frameworkanubis],
+ [group: 'io.mifos.customer', name: 'api', version: versions.mifoscustomer],
[group: 'com.google.code.gson', name: 'gson'],
[group: 'io.mifos.core', name: 'api', version: versions.frameworkapi],
[group: 'io.mifos.core', name: 'lang', version: versions.frameworklang],
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 07c8d76..9ce0d61 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -18,6 +18,7 @@
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import io.mifos.core.lang.ServiceException;
+import io.mifos.customer.api.v1.client.CustomerManager;
import io.mifos.individuallending.api.v1.domain.caseinstance.CaseParameters;
import io.mifos.individuallending.api.v1.domain.product.ChargeIdentifiers;
import io.mifos.individuallending.api.v1.domain.product.ChargeProportionalDesignator;
@@ -58,18 +59,21 @@
final static private String INDIVIDUAL_LENDING_PACKAGE = "io.mifos.individuallending.api.v1";
private final CaseParametersRepository caseParametersRepository;
private final CostComponentService costComponentService;
+ private final CustomerManager customerManager;
private final IndividualLendingCommandDispatcher individualLendingCommandDispatcher;
private final Gson gson;
@Autowired
IndividualLendingPatternFactory(
- final CaseParametersRepository caseParametersRepository,
- final CostComponentService costComponentService,
- final IndividualLendingCommandDispatcher individualLendingCommandDispatcher,
- @Qualifier(ServiceConstants.GSON_NAME) final Gson gson)
+ final CaseParametersRepository caseParametersRepository,
+ final CostComponentService costComponentService,
+ final CustomerManager customerManager,
+ final IndividualLendingCommandDispatcher individualLendingCommandDispatcher,
+ @Qualifier(ServiceConstants.GSON_NAME) final Gson gson)
{
this.caseParametersRepository = caseParametersRepository;
this.costComponentService = costComponentService;
+ this.customerManager = customerManager;
this.individualLendingCommandDispatcher = individualLendingCommandDispatcher;
this.gson = gson;
}
@@ -240,9 +244,18 @@
return ret;
}
+ @Override
+ public void checkParameters(final String parameters) {
+ final CaseParameters caseParameters = gson.fromJson(parameters, CaseParameters.class);
+ final String customerIdentifier = caseParameters.getCustomerIdentifier();
+ if (!customerManager.isCustomerInGoodStanding(customerIdentifier))
+ throw ServiceException.badRequest("Customer ''{0}'' is either not a customer or is not in good standing.");
+ }
+
@Transactional
@Override
public void persistParameters(final Long caseId, final String parameters) {
+ checkParameters(parameters);
final CaseParameters caseParameters = gson.fromJson(parameters, CaseParameters.class);
final CaseParametersEntity caseParametersEntity = CaseParametersMapper.map(caseId, caseParameters);
caseParametersRepository.save(caseParametersEntity);
@@ -277,7 +290,8 @@
@Transactional
@Override
- public void changeParameters(Long caseId, String parameters) {
+ public void changeParameters(final Long caseId, final String parameters) {
+ checkParameters(parameters);
final CaseParameters caseParameters = gson.fromJson(parameters, CaseParameters.class);
final CaseParametersEntity oldCaseParameters = caseParametersRepository.findByCaseId(caseId)
.orElseThrow(() -> new IllegalArgumentException("Case id does not represent an individual loan: " + caseId));
diff --git a/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
index adcb3b3..9668332 100644
--- a/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
+++ b/service/src/main/java/io/mifos/portfolio/service/config/PortfolioServiceConfiguration.java
@@ -25,6 +25,7 @@
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.individuallending.IndividualLendingConfiguration;
import io.mifos.portfolio.service.ServiceConstants;
import io.mifos.rhythm.api.v1.client.RhythmManager;
@@ -60,7 +61,7 @@
})
@EnableJpaRepositories(basePackages = "io.mifos.portfolio.service.internal.repository")
@EntityScan(basePackages = "io.mifos.portfolio.service.internal.repository")
-@EnableFeignClients(clients = {LedgerManager.class, RhythmManager.class})
+@EnableFeignClients(clients = {LedgerManager.class, RhythmManager.class, CustomerManager.class})
@RibbonClient(name = "portfolio-v1")
@EnableApplicationName
@Import(IndividualLendingConfiguration.class)
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/checker/CaseChecker.java b/service/src/main/java/io/mifos/portfolio/service/internal/checker/CaseChecker.java
new file mode 100644
index 0000000..ab8b20b
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/checker/CaseChecker.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.portfolio.service.internal.checker;
+
+import io.mifos.core.lang.ServiceException;
+import io.mifos.portfolio.api.v1.domain.Case;
+import io.mifos.portfolio.service.internal.pattern.PatternFactoryRegistry;
+import io.mifos.portfolio.service.internal.repository.ProductEntity;
+import io.mifos.portfolio.service.internal.repository.ProductRepository;
+import io.mifos.portfolio.service.internal.service.CaseService;
+import io.mifos.portfolio.service.internal.service.ProductService;
+import io.mifos.products.spi.PatternFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+/**
+ * @author Myrle Krantz
+ */
+@Component
+public class CaseChecker {
+ private final CaseService caseService;
+ private final ProductService productService;
+ private final ProductRepository productRepository;
+ private final PatternFactoryRegistry patternFactoryRegistry;
+
+ @Autowired
+ public CaseChecker(final CaseService caseService,
+ final ProductService productService,
+ final ProductRepository productRepository,
+ final PatternFactoryRegistry patternFactoryRegistry) {
+ this.caseService = caseService;
+ this.productService = productService;
+ this.productRepository = productRepository;
+ this.patternFactoryRegistry = patternFactoryRegistry;
+ }
+
+ public void checkForCreate(final String productIdentifier, final Case instance) {
+ caseService.findByIdentifier(productIdentifier, instance.getIdentifier())
+ .ifPresent(x -> {throw ServiceException.conflict("Duplicate identifier: " + productIdentifier + "." + x.getIdentifier());});
+
+ final Optional<Boolean> productEnabled = productService.findEnabledByIdentifier(productIdentifier);
+ if (!productEnabled.orElseThrow(() -> ServiceException.internalError("Product should exist, but doesn't"))) {
+ throw ServiceException.badRequest("Product must be enabled before cases for it can be created: " + productIdentifier);}
+
+ getPatternFactory(productIdentifier).checkParameters(instance.getParameters());
+ }
+
+ public void checkForChange(final String productIdentifier, final Case instance) {
+ getPatternFactory(productIdentifier).checkParameters(instance.getParameters());
+ }
+
+ private PatternFactory getPatternFactory(final String productIdentifier) {
+ return productRepository.findByIdentifier(productIdentifier)
+ .map(ProductEntity::getPatternPackage)
+ .flatMap(patternFactoryRegistry::getPatternFactoryForPackage)
+ .orElseThrow(() -> new IllegalArgumentException("Case references unsupported product type."));
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/CaseCommandHandler.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/CaseCommandHandler.java
index a8700c0..aa91b80 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/CaseCommandHandler.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/CaseCommandHandler.java
@@ -80,7 +80,7 @@
return new CaseEvent(caseInstance.getProductIdentifier(), caseInstance.getIdentifier());
}
- private PatternFactory getPatternFactory(String productIdentifier) {
+ private PatternFactory getPatternFactory(final String productIdentifier) {
return productRepository.findByIdentifier(productIdentifier)
.map(ProductEntity::getPatternPackage)
.flatMap(patternFactoryRegistry::getPatternFactoryForPackage)
diff --git a/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java b/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
index f2a4ed5..3117f67 100644
--- a/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
+++ b/service/src/main/java/io/mifos/portfolio/service/rest/CaseRestController.java
@@ -25,6 +25,7 @@
import io.mifos.portfolio.api.v1.domain.CasePage;
import io.mifos.portfolio.api.v1.domain.Command;
import io.mifos.portfolio.api.v1.domain.CostComponent;
+import io.mifos.portfolio.service.internal.checker.CaseChecker;
import io.mifos.portfolio.service.internal.command.ChangeCaseCommand;
import io.mifos.portfolio.service.internal.command.CreateCaseCommand;
import io.mifos.portfolio.service.internal.service.CaseService;
@@ -40,7 +41,6 @@
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.List;
-import java.util.Optional;
import java.util.Set;
/**
@@ -53,17 +53,21 @@
private final CommandGateway commandGateway;
private final CaseService caseService;
+ private final CaseChecker caseChecker;
private final ProductService productService;
private final TaskInstanceService taskInstanceService;
- @Autowired public CaseRestController(
+ @Autowired
+ public CaseRestController(
final CommandGateway commandGateway,
final CaseService caseService,
+ final CaseChecker caseChecker,
final ProductService productService,
final TaskInstanceService taskInstanceService) {
super();
this.commandGateway = commandGateway;
this.caseService = caseService;
+ this.caseChecker = caseChecker;
this.productService = productService;
this.taskInstanceService = taskInstanceService;
}
@@ -94,13 +98,6 @@
{
checkThatProductExists(productIdentifier);
- caseService.findByIdentifier(productIdentifier, instance.getIdentifier())
- .ifPresent(x -> {throw ServiceException.conflict("Duplicate identifier: " + productIdentifier + "." + x.getIdentifier());});
-
- final Optional<Boolean> productEnabled = productService.findEnabledByIdentifier(productIdentifier);
- if (!productEnabled.orElseThrow(() -> ServiceException.internalError("Product should exist, but doesn't"))) {
- throw ServiceException.badRequest("Product must be enabled before cases for it can be created: " + productIdentifier);}
-
if (!instance.getProductIdentifier().equals(productIdentifier))
throw ServiceException.badRequest("Product identifier in request body must match product identifier in request path.");
@@ -120,6 +117,8 @@
if (instance.getLastModifiedOn() != null)
throw ServiceException.badRequest("LastModifiedOn must 'null' be upon initial creation.");
+ caseChecker.checkForCreate(productIdentifier, instance);
+
this.commandGateway.process(new CreateCaseCommand(instance));
return new ResponseEntity<>(HttpStatus.ACCEPTED);
}
@@ -159,6 +158,8 @@
if (!caseIdentifier.equals(instance.getIdentifier()))
throw ServiceException.badRequest("Instance identifier may not be changed.");
+ caseChecker.checkForChange(productIdentifier, instance);
+
this.commandGateway.process(new ChangeCaseCommand(instance));
return new ResponseEntity<>(HttpStatus.ACCEPTED);
//TODO: Make sure case can't be changed from certain states.
@@ -245,5 +246,5 @@
throw ServiceException.notFound("Product with identifier ''{0}'' doesn''t exist.", productIdentifier);
}
- //TODO: check that case parameters are within product parameters in put and post.
+ //TODO: createCheck that case parameters are within product parameters in put and post.
}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/products/spi/PatternFactory.java b/service/src/main/java/io/mifos/products/spi/PatternFactory.java
index 5357e39..4b9f3c2 100644
--- a/service/src/main/java/io/mifos/products/spi/PatternFactory.java
+++ b/service/src/main/java/io/mifos/products/spi/PatternFactory.java
@@ -32,6 +32,7 @@
public interface PatternFactory {
Pattern pattern();
List<ChargeDefinition> charges();
+ void checkParameters(String parameters);
void persistParameters(Long caseId, String parameters);
void changeParameters(Long caseId, String parameters);
Optional<String> getParameters(Long caseId, int minorCurrencyUnitDigits);
diff --git a/shared.gradle b/shared.gradle
index 3b74547..410d57d 100644
--- a/shared.gradle
+++ b/shared.gradle
@@ -12,6 +12,7 @@
frameworkasync : '0.1.0-BUILD-SNAPSHOT',
frameworkaccounting : '0.1.0-BUILD-SNAPSHOT',
mifosrhythm : '0.1.0-BUILD-SNAPSHOT',
+ mifoscustomer : '0.1.0-BUILD-SNAPSHOT',
validator : '5.3.0.Final',
javamoneylib : '0.9-SNAPSHOT'
]