Merge branch 'develop' into arrearsAndWriteOff
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/PermittableGroupIds.java b/api/src/main/java/io/mifos/portfolio/api/v1/PermittableGroupIds.java
index 48b294c..75f8a7f 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/PermittableGroupIds.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/PermittableGroupIds.java
@@ -21,7 +21,7 @@
@SuppressWarnings("unused")
public interface PermittableGroupIds {
String PRODUCT_OPERATIONS_MANAGEMENT = "portfolio__v1__products__enable";
- String PRODUCT_LOSS_PROVISIONING_MANAGEMENT = "portfolio__v1__products__lossprov";
+ String PRODUCT_LOSS_PROVISIONING_MANAGEMENT = "portfolio__v1__products__lossprv";
String PRODUCT_MANAGEMENT = "portfolio__v1__products";
String CASE_MANAGEMENT = "portfolio__v1__case";
}
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/validation/CheckValidLossProvisionList.java b/api/src/main/java/io/mifos/portfolio/api/v1/validation/CheckValidLossProvisionList.java
index 3085033..adf9988 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/validation/CheckValidLossProvisionList.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/validation/CheckValidLossProvisionList.java
@@ -19,8 +19,9 @@
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
-import java.math.BigDecimal;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
/**
* @author Myrle Krantz
@@ -35,10 +36,11 @@
public boolean isValid(
final List<LossProvisionStep> value,
final ConstraintValidatorContext context) {
- final BigDecimal sum = value.stream()
- .map(LossProvisionStep::getPercentProvision)
- .map(x -> x.setScale(2, BigDecimal.ROUND_HALF_EVEN))
- .reduce(BigDecimal.ZERO, BigDecimal::add);
- return sum.compareTo(BigDecimal.valueOf(100_00, 2)) == 0;
+ if (value == null) return false;
+ final Map<Integer, Long> configurationsPerDay = value.stream()
+ .collect(Collectors.groupingBy(LossProvisionStep::getDaysLate, Collectors.counting()));
+ final boolean moreThanOneConfigurationForAtLeastOneDay = configurationsPerDay.values().stream()
+ .anyMatch(x -> x > 1);
+ return !moreThanOneConfigurationForAtLeastOneDay;
}
}
\ No newline at end of file
diff --git a/api/src/test/java/io/mifos/individuallending/api/v1/domain/product/LossProvisionConfigurationTest.java b/api/src/test/java/io/mifos/individuallending/api/v1/domain/product/LossProvisionConfigurationTest.java
index ba6e24b..5139310 100644
--- a/api/src/test/java/io/mifos/individuallending/api/v1/domain/product/LossProvisionConfigurationTest.java
+++ b/api/src/test/java/io/mifos/individuallending/api/v1/domain/product/LossProvisionConfigurationTest.java
@@ -36,7 +36,7 @@
final LossProvisionConfiguration ret = new LossProvisionConfiguration();
final List<LossProvisionStep> lossProvisionSteps = new ArrayList<>();
lossProvisionSteps.add(new LossProvisionStep(0, BigDecimal.ONE));
- lossProvisionSteps.add(new LossProvisionStep(1, BigDecimal.valueOf(9)));
+ lossProvisionSteps.add(new LossProvisionStep(1, BigDecimal.valueOf(10)));
lossProvisionSteps.add(new LossProvisionStep(10, BigDecimal.valueOf(20)));
lossProvisionSteps.add(new LossProvisionStep(50, BigDecimal.valueOf(70)));
ret.setLossProvisionSteps(lossProvisionSteps);
@@ -50,15 +50,12 @@
ret.add(new ValidationTestCase<LossProvisionConfiguration>("valid"));
ret.add(new ValidationTestCase<LossProvisionConfiguration>("emptyList")
.adjustment(x -> x.setLossProvisionSteps(Collections.emptyList()))
- .valid(false));
+ .valid(true));
ret.add(new ValidationTestCase<LossProvisionConfiguration>("nullList")
- .adjustment(x -> x.setLossProvisionSteps(Collections.emptyList()))
+ .adjustment(x -> x.setLossProvisionSteps(null))
.valid(false));
- ret.add(new ValidationTestCase<LossProvisionConfiguration>("sumTooSmall")
- .adjustment(x -> x.getLossProvisionSteps().get(0).setPercentProvision(BigDecimal.valueOf(0.1)))
- .valid(false));
- ret.add(new ValidationTestCase<LossProvisionConfiguration>("sumTooLarge")
- .adjustment(x -> x.getLossProvisionSteps().get(3).setPercentProvision(BigDecimal.valueOf(71)))
+ ret.add(new ValidationTestCase<LossProvisionConfiguration>("moreThanOneValuesForOneDay")
+ .adjustment(x -> x.getLossProvisionSteps().add(new LossProvisionStep(0, BigDecimal.valueOf(0.1))))
.valid(false));
return ret;
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestLossProvisionSteps.java b/component-test/src/main/java/io/mifos/portfolio/TestLossProvisionSteps.java
index f86ef33..349f8bd 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestLossProvisionSteps.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestLossProvisionSteps.java
@@ -24,6 +24,7 @@
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -33,16 +34,44 @@
@Test
public void shouldChangeAndGetLossProvisionSteps() throws InterruptedException {
final Product product = createAdjustedProduct(x -> {});
+
final List<LossProvisionStep> lossProvisionSteps = new ArrayList<>();
lossProvisionSteps.add(new LossProvisionStep(0, BigDecimal.valueOf(1_00, 2)));
lossProvisionSteps.add(new LossProvisionStep(1, BigDecimal.valueOf(9_00, 2)));
lossProvisionSteps.add(new LossProvisionStep(30, BigDecimal.valueOf(35_00, 2)));
lossProvisionSteps.add(new LossProvisionStep(60, BigDecimal.valueOf(55_00, 2)));
final LossProvisionConfiguration lossProvisionConfiguration = new LossProvisionConfiguration(lossProvisionSteps);
- individualLending.changeLossProvisionConfiguration(product.getIdentifier(), lossProvisionConfiguration);
+ individualLending.changeLossProvisionConfiguration(product.getIdentifier(), lossProvisionConfiguration);
Assert.assertTrue(eventRecorder.wait(IndividualLoanEventConstants.PUT_LOSS_PROVISION_STEPS, product.getIdentifier()));
+ Thread.sleep(2000);
+
final LossProvisionConfiguration lossProvisionConfigurationAsSaved = individualLending.getLossProvisionConfiguration(product.getIdentifier());
Assert.assertEquals(lossProvisionConfiguration, lossProvisionConfigurationAsSaved);
+
+
+ final List<LossProvisionStep> lossProvisionSteps2 = new ArrayList<>();
+ lossProvisionSteps2.add(new LossProvisionStep(0, BigDecimal.valueOf(2_00, 2)));
+ lossProvisionSteps2.add(new LossProvisionStep(1, BigDecimal.valueOf(15_00, 2)));
+ lossProvisionSteps2.add(new LossProvisionStep(30, BigDecimal.valueOf(35_00, 2)));
+ lossProvisionSteps2.add(new LossProvisionStep(60, BigDecimal.valueOf(55_00, 2)));
+ final LossProvisionConfiguration lossProvisionConfiguration2 = new LossProvisionConfiguration(lossProvisionSteps2);
+
+ individualLending.changeLossProvisionConfiguration(product.getIdentifier(), lossProvisionConfiguration2);
+ Assert.assertTrue(eventRecorder.wait(IndividualLoanEventConstants.PUT_LOSS_PROVISION_STEPS, product.getIdentifier()));
+ Thread.sleep(2000);
+
+ final LossProvisionConfiguration lossProvisionConfiguration2AsSaved = individualLending.getLossProvisionConfiguration(product.getIdentifier());
+ Assert.assertEquals(lossProvisionConfiguration2, lossProvisionConfiguration2AsSaved);
+
+
+ final LossProvisionConfiguration lossProvisionConfiguration3 = new LossProvisionConfiguration(Collections.emptyList());
+
+ individualLending.changeLossProvisionConfiguration(product.getIdentifier(), lossProvisionConfiguration3);
+ Assert.assertTrue(eventRecorder.wait(IndividualLoanEventConstants.PUT_LOSS_PROVISION_STEPS, product.getIdentifier()));
+ Thread.sleep(2000);
+
+ //TODO: final LossProvisionConfiguration lossProvisionConfiguration3AsSaved = individualLending.getLossProvisionConfiguration(product.getIdentifier());
+ //Assert.assertEquals(lossProvisionConfiguration3, lossProvisionConfiguration3AsSaved);
}
}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/handler/LossProvisionStepsCommandHandler.java b/service/src/main/java/io/mifos/individuallending/internal/command/handler/LossProvisionStepsCommandHandler.java
index 31cd9d8..126fa9a 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/command/handler/LossProvisionStepsCommandHandler.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/command/handler/LossProvisionStepsCommandHandler.java
@@ -30,7 +30,9 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
-import java.util.stream.Stream;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
/**
@@ -57,11 +59,28 @@
public String process(final ChangeLossProvisionSteps command) {
final ProductEntity productEntity = productRepository.findByIdentifier(command.getProductIdentifier())
.orElseThrow(() -> ServiceException.notFound("Product not found ''{0}''.", command.getProductIdentifier()));
- final Stream<LossProvisionStepEntity> lossProvisionSteps = lossProvisionStepRepository.findByProductId(productEntity.getId());
- lossProvisionSteps.forEach(lossProvisionStepRepository::delete);
- command.getLossProvisionConfiguration().getLossProvisionSteps().stream()
- .map(lossProvisionStep -> LossProvisionStepMapper.map(productEntity.getId(), lossProvisionStep))
- .forEach(lossProvisionStepRepository::save);
+ final Map<Integer, LossProvisionStepEntity> existingLossProvisionSteps =
+ lossProvisionStepRepository.findByProductId(productEntity.getId())
+ .collect(Collectors.toMap(LossProvisionStepEntity::getDaysLate, Function.identity()));
+
+ final Map<Integer, LossProvisionStepEntity> newLossProvisionSteps =
+ command.getLossProvisionConfiguration().getLossProvisionSteps().stream()
+ .map(newLossProvisionStep -> {
+ final LossProvisionStepEntity existingLossProvisionStepEntity = existingLossProvisionSteps.get(newLossProvisionStep.getDaysLate());
+ if (existingLossProvisionStepEntity != null) {
+ existingLossProvisionStepEntity.setPercentProvision(newLossProvisionStep.getPercentProvision());
+ return existingLossProvisionStepEntity;
+ } else {
+ return LossProvisionStepMapper.map(productEntity.getId(), newLossProvisionStep);
+ }
+ })
+ .collect(Collectors.toMap(LossProvisionStepEntity::getDaysLate, Function.identity()));
+ newLossProvisionSteps.values().forEach(lossProvisionStepRepository::save);
+
+ existingLossProvisionSteps.forEach((daysLate, lossProvisionStep) -> {
+ if (newLossProvisionSteps.get(daysLate) == null)
+ lossProvisionStepRepository.delete(lossProvisionStep);
+ });
return command.getProductIdentifier();
}
diff --git a/service/src/main/java/io/mifos/individuallending/internal/repository/LossProvisionStepEntity.java b/service/src/main/java/io/mifos/individuallending/internal/repository/LossProvisionStepEntity.java
index a74a8ea..0b90ae4 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/repository/LossProvisionStepEntity.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/repository/LossProvisionStepEntity.java
@@ -17,7 +17,6 @@
import javax.persistence.*;
import java.math.BigDecimal;
-import java.util.Objects;
/**
* @author Myrle Krantz
@@ -71,18 +70,4 @@
public void setPercentProvision(BigDecimal percentProvision) {
this.percentProvision = percentProvision;
}
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- LossProvisionStepEntity that = (LossProvisionStepEntity) o;
- return Objects.equals(productId, that.productId) &&
- Objects.equals(daysLate, that.daysLate);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(productId, daysLate);
- }
}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/individuallending/internal/repository/LossProvisionStepRepository.java b/service/src/main/java/io/mifos/individuallending/internal/repository/LossProvisionStepRepository.java
index 810412e..3f5fff6 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/repository/LossProvisionStepRepository.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/repository/LossProvisionStepRepository.java
@@ -26,7 +26,9 @@
*/
@Repository
public interface LossProvisionStepRepository extends JpaRepository<LossProvisionStepEntity, Long> {
- Stream<LossProvisionStepEntity> findByProductId(Long id);
+ Stream<LossProvisionStepEntity> findByProductId(Long productId);
- Optional<LossProvisionStepEntity> findByProductIdAndDaysLate(Long id, int daysLate);
+ Stream<LossProvisionStepEntity> findByProductIdOrderByDaysLateAsc(Long productId);
+
+ Optional<LossProvisionStepEntity> findByProductIdAndDaysLate(Long productId, int daysLate);
}
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/LossProvisionStepService.java b/service/src/main/java/io/mifos/individuallending/internal/service/LossProvisionStepService.java
index a079ecf..80d4dc9 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/LossProvisionStepService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/LossProvisionStepService.java
@@ -54,7 +54,7 @@
final Long productId = productRepository.findByIdentifier(productIdentifier)
.orElseThrow(() -> ServiceException.notFound("Product ''{}'' doesn''t exist.", productIdentifier))
.getId();
- return lossProvisionStepRepository.findByProductId(productId)
+ return lossProvisionStepRepository.findByProductIdOrderByDaysLateAsc(productId)
.map(LossProvisionStepMapper::map)
.collect(Collectors.toList());
}