MIFOSX-2426 : added support for Interest Rate Charts based on Amount

This commit is contained in:
pramod
2016-03-04 15:57:27 +05:30
parent 14499a26fb
commit 37dfe1a0bf
27 changed files with 1645 additions and 431 deletions
@@ -142,7 +142,12 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
public static Calendar createRepeatingCalendar(final String title, final LocalDate startDate, final Integer typeId,
final CalendarFrequencyType frequencyType, final Integer interval, final Integer repeatsOnDay) {
final String recurrence = constructRecurrence(frequencyType, interval, repeatsOnDay);
return createRepeatingCalendar(title, startDate, typeId, recurrence);
}
public static Calendar createRepeatingCalendar(final String title, final LocalDate startDate, final Integer typeId,
final String recurrence) {
final String description = null;
final String location = null;
final LocalDate endDate = null;
@@ -151,8 +156,6 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
final Integer remindById = null;
final Integer firstReminder = null;
final Integer secondReminder = null;
final String recurrence = constructRecurrence(frequencyType, interval, repeatsOnDay);
return new Calendar(title, description, location, startDate, endDate, duration, typeId, repeating, recurrence, remindById,
firstReminder, secondReminder);
}
@@ -233,7 +236,7 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
}
public Map<String, Object> update(final JsonCommand command, final Boolean areActiveEntitiesSynced ) {
public Map<String, Object> update(final JsonCommand command, final Boolean areActiveEntitiesSynced) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
@@ -345,7 +348,7 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
* calendar then do not allow to change meeting frequency
*/
if ( areActiveEntitiesSynced && !CalendarUtils.isFrequencySame(this.recurrence, newRecurrence)) {
if (areActiveEntitiesSynced && !CalendarUtils.isFrequencySame(this.recurrence, newRecurrence)) {
final String defaultUserMessage = "Update of meeting frequency is not supported";
throw new CalendarParameterUpdateNotSupportedException("meeting.frequency", defaultUserMessage);
}
@@ -44,6 +44,7 @@ public class InterestRateChartApiConstants {
public static final String endDateParamName = "endDate";
public static final String productIdParamName = "productId";
public static final String productNameParamName = "productName";
public static final String isPrimaryGroupingByAmountParamName = "isPrimaryGroupingByAmount";
// interest rate chart Slabs parameters
// public static final String periodTypeParamName = "periodType";
@@ -63,13 +64,14 @@ public class InterestRateChartApiConstants {
public static final String deleteParamName = "delete";
public static final Set<String> INTERESTRATE_CHART_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, productIdParamName,
chartSlabs));
dateFormatParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, productIdParamName, chartSlabs,
isPrimaryGroupingByAmountParamName));
public static final Set<String> INTERESTRATE_CHART_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, idParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, chartSlabs,
deleteParamName));
deleteParamName, isPrimaryGroupingByAmountParamName));
public static final Set<String> INTERESTRATE_CHART_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, idParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, chartSlabs));
dateFormatParamName, idParamName, nameParamName, descriptionParamName, fromDateParamName, endDateParamName, chartSlabs,
isPrimaryGroupingByAmountParamName));
}
@@ -18,9 +18,8 @@
*/
package org.apache.fineract.portfolio.interestratechart.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
@@ -38,9 +37,10 @@ public class InterestRateChartData {
private final LocalDate endDate;
private final Long productId;
private final String productName;
private final boolean isPrimaryGroupingByAmount;
// associations
private Set<InterestRateChartSlabData> chartSlabs;
private Collection<InterestRateChartSlabData> chartSlabs;
// template
private final Collection<EnumOptionData> periodTypes;
@@ -53,9 +53,9 @@ public class InterestRateChartData {
private final Collection<CodeValueData> clientClassificationOptions;
public static InterestRateChartData instance(Long id, String name, String description, LocalDate fromDate, LocalDate endDate,
Long savingsProductId, String savingsProductName) {
boolean isPrimaryGroupingByAmount, Long savingsProductId, String savingsProductName) {
Collection<EnumOptionData> periodTypes = null;
Set<InterestRateChartSlabData> chartSlabs = null;
Collection<InterestRateChartSlabData> chartSlabs = null;
final Collection<EnumOptionData> entityTypeOptions = null;
final Collection<EnumOptionData> attributeNameOptions = null;
final Collection<EnumOptionData> conditionTypeOptions = null;
@@ -63,17 +63,18 @@ public class InterestRateChartData {
final Collection<CodeValueData> genderOptions = null;
final Collection<CodeValueData> clientTypeOptions = null;
final Collection<CodeValueData> clientClassificationOptions = null;
return new InterestRateChartData(id, name, description, fromDate, endDate, savingsProductId, savingsProductName, chartSlabs,
periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions,
clientTypeOptions, clientClassificationOptions);
return new InterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, savingsProductId,
savingsProductName, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static InterestRateChartData withSlabs(InterestRateChartData interestRateChartData, Set<InterestRateChartSlabData> chartSlabs) {
public static InterestRateChartData withSlabs(InterestRateChartData interestRateChartData, Collection<InterestRateChartSlabData> chartSlabs) {
return new InterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description,
interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.productId,
interestRateChartData.productName, chartSlabs, interestRateChartData.periodTypes, interestRateChartData.entityTypeOptions,
interestRateChartData.attributeNameOptions, interestRateChartData.conditionTypeOptions,
interestRateChartData.incentiveTypeOptions, interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions,
interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.isPrimaryGroupingByAmount,
interestRateChartData.productId, interestRateChartData.productName, chartSlabs, interestRateChartData.periodTypes,
interestRateChartData.entityTypeOptions, interestRateChartData.attributeNameOptions,
interestRateChartData.conditionTypeOptions, interestRateChartData.incentiveTypeOptions,
interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions,
interestRateChartData.clientClassificationOptions);
}
@@ -83,9 +84,10 @@ public class InterestRateChartData {
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
return new InterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description,
interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.productId,
interestRateChartData.productName, interestRateChartData.chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions,
conditionTypeOptions, incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.isPrimaryGroupingByAmount,
interestRateChartData.productId, interestRateChartData.productName, interestRateChartData.chartSlabs, periodTypes,
entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, clientTypeOptions,
clientClassificationOptions);
}
public static InterestRateChartData template(Collection<EnumOptionData> periodTypes,
@@ -98,26 +100,28 @@ public class InterestRateChartData {
final String description = null;
final LocalDate fromDate = null;
final LocalDate endDate = null;
final boolean isPrimaryGroupingByAmount = false;
final Long savingsProductId = null;
final String savingsProductName = null;
final Set<InterestRateChartSlabData> chartSlabs = null;
final Collection<InterestRateChartSlabData> chartSlabs = null;
return new InterestRateChartData(id, name, description, fromDate, endDate, savingsProductId, savingsProductName, chartSlabs,
periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions,
clientTypeOptions, clientClassificationOptions);
return new InterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, savingsProductId,
savingsProductName, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
private InterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate, Long savingsProductId,
String savingsProductName, Set<InterestRateChartSlabData> chartSlabs, Collection<EnumOptionData> periodTypes,
final Collection<EnumOptionData> entityTypeOptions, final Collection<EnumOptionData> attributeNameOptions,
final Collection<EnumOptionData> conditionTypeOptions, final Collection<EnumOptionData> incentiveTypeOptions,
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
private InterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate,
boolean isPrimaryGroupingByAmount, Long savingsProductId, String savingsProductName, Collection<InterestRateChartSlabData> chartSlabs,
Collection<EnumOptionData> periodTypes, final Collection<EnumOptionData> entityTypeOptions,
final Collection<EnumOptionData> attributeNameOptions, final Collection<EnumOptionData> conditionTypeOptions,
final Collection<EnumOptionData> incentiveTypeOptions, final Collection<CodeValueData> genderOptions,
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions) {
this.id = id;
this.name = name;
this.description = description;
this.fromDate = fromDate;
this.endDate = endDate;
this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount;
this.chartSlabs = chartSlabs;
this.productId = savingsProductId;
this.productName = savingsProductName;
@@ -133,7 +137,7 @@ public class InterestRateChartData {
public void addChartSlab(final InterestRateChartSlabData chartSlab) {
if (this.chartSlabs == null) {
this.chartSlabs = new HashSet<>();
this.chartSlabs = new ArrayList<>();
}
this.chartSlabs.add(chartSlab);
@@ -159,7 +163,7 @@ public class InterestRateChartData {
return this.description;
}
public Set<InterestRateChartSlabData> chartSlabs() {
public Collection<InterestRateChartSlabData> chartSlabs() {
return this.chartSlabs;
}
@@ -195,4 +199,9 @@ public class InterestRateChartData {
return this.clientClassificationOptions;
}
public boolean isPrimaryGroupingByAmount() {
return this.isPrimaryGroupingByAmount;
}
}
@@ -26,6 +26,7 @@ import static org.apache.fineract.portfolio.interestratechart.InterestRateChartA
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.idParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.isPrimaryGroupingByAmountParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.productIdParamName;
@@ -98,6 +99,11 @@ public class InterestRateChartDataValidator {
toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element);
baseDataValidator.reset().parameter(endDateParamName).value(toDate).notNull();
}
Boolean isPrimaryGroupingByAmount = this.fromApiJsonHelper.extractBooleanNamed(isPrimaryGroupingByAmountParamName, element);
if (isPrimaryGroupingByAmount == null) {
isPrimaryGroupingByAmount = false;
}
if (fromDate != null && toDate != null) {
if (fromDate.isAfter(toDate)) {
@@ -106,7 +112,7 @@ public class InterestRateChartDataValidator {
}
// validate chart Slabs
validateChartSlabs(element, baseDataValidator);
validateChartSlabs(element, baseDataValidator, isPrimaryGroupingByAmount);
}
public void validateUpdate(final String json) {
@@ -151,6 +157,11 @@ public class InterestRateChartDataValidator {
toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element);
baseDataValidator.reset().parameter(endDateParamName).value(toDate).notNull();
}
Boolean isPrimaryGroupingByAmount = this.fromApiJsonHelper.extractBooleanNamed(isPrimaryGroupingByAmountParamName, element);
if (isPrimaryGroupingByAmount == null) {
isPrimaryGroupingByAmount = false;
}
if (fromDate != null && toDate != null) {
if (fromDate.isAfter(toDate)) {
@@ -158,7 +169,7 @@ public class InterestRateChartDataValidator {
}
}
validateChartSlabs(element, baseDataValidator);
validateChartSlabs(element, baseDataValidator, isPrimaryGroupingByAmount);
}
@@ -166,7 +177,7 @@ public class InterestRateChartDataValidator {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
private void validateChartSlabs(JsonElement element, DataValidatorBuilder baseDataValidator) {
private void validateChartSlabs(JsonElement element, DataValidatorBuilder baseDataValidator,final boolean isPrimaryGroupingByAmount) {
if (element.isJsonObject()) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
@@ -178,9 +189,9 @@ public class InterestRateChartDataValidator {
if (this.fromApiJsonHelper.parameterExists(idParamName, interstRateChartElement)) {
final Long id = this.fromApiJsonHelper.extractLongNamed(idParamName, interstRateChartElement);
baseDataValidator.reset().parameter(idParamName).value(id).notNull().integerGreaterThanZero();
this.chartSlabDataValidator.validateChartSlabsUpdate(interstRateChartElement, baseDataValidator, locale);
this.chartSlabDataValidator.validateChartSlabsUpdate(interstRateChartElement, baseDataValidator, locale, isPrimaryGroupingByAmount);
} else {
this.chartSlabDataValidator.validateChartSlabsCreate(interstRateChartElement, baseDataValidator, locale);
this.chartSlabDataValidator.validateChartSlabsCreate(interstRateChartElement, baseDataValidator, locale, isPrimaryGroupingByAmount);
}
}
}
@@ -84,12 +84,14 @@ public class InterestRateChartSlabDataValidator {
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element);
baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank().notExceedingLengthOf(3);
validateChartSlabsCreate(element, baseDataValidator, locale);
final Boolean isPrimaryGroupingByAmount = null;
validateChartSlabsCreate(element, baseDataValidator, locale, isPrimaryGroupingByAmount);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateChartSlabsCreate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale) {
public void validateChartSlabsCreate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale,
final Boolean isPrimaryGroupingByAmount) {
if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) {
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
@@ -97,17 +99,25 @@ public class InterestRateChartSlabDataValidator {
}
final Integer periodType = this.fromApiJsonHelper.extractIntegerNamed(periodTypeParamName, element, locale);
baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull()
.isOneOfTheseValues(PeriodFrequencyType.integerValues());
if (this.fromApiJsonHelper.parameterExists(periodTypeParamName, element)) {
baseDataValidator.reset().parameter(periodTypeParamName).value(periodType)
.isOneOfTheseValues(PeriodFrequencyType.integerValues());
}
Integer toPeriod = null;
final Integer fromPeriod = this.fromApiJsonHelper.extractIntegerNamed(fromPeriodParamName, element, locale);
baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull().integerZeroOrGreater();
if (this.fromApiJsonHelper.parameterExists(fromPeriodParamName, element)) {
baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).integerZeroOrGreater();
}
if ((isPrimaryGroupingByAmount != null && !isPrimaryGroupingByAmount) || (periodType != null || fromPeriod != null)) {
baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull();
baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull();
}
if (this.fromApiJsonHelper.parameterExists(toPeriodParamName, element)) {
toPeriod = this.fromApiJsonHelper.extractIntegerNamed(toPeriodParamName, element, locale);
baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).notNull().integerZeroOrGreater();
baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).integerGreaterThanZero();
}
if (fromPeriod != null && toPeriod != null) {
@@ -115,21 +125,26 @@ public class InterestRateChartSlabDataValidator {
baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("fromperiod.greater.than.to.period");
}
}
BigDecimal amountRangeFrom = null;
BigDecimal amountRangeTo = null;
final BigDecimal amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale);
final BigDecimal amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale);
if (this.fromApiJsonHelper.parameterExists(amountRangeFromParamName, element)) {
amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale);
baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull().positiveAmount();
baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).zeroOrPositiveAmount();
if (isPrimaryGroupingByAmount != null && isPrimaryGroupingByAmount) {
baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull();
}
}
if (this.fromApiJsonHelper.parameterExists(amountRangeToParamName, element)) {
amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale);
baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).notNull().positiveAmount();
baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).positiveAmount();
}
if (amountRangeFrom == null && fromPeriod == null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("fromperiod.or.amountRangeFrom.required");
}
if (amountRangeFrom != null && amountRangeTo != null) {
if (amountRangeFrom.compareTo(amountRangeTo) > 1) {
baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("fromperiod.greater.than.toperiod");
baseDataValidator.parameter(amountRangeFromParamName).value(fromPeriod).failWithCode("from.amount.greater.than.to.amount");
}
}
@@ -152,12 +167,14 @@ public class InterestRateChartSlabDataValidator {
final JsonElement element = this.fromApiJsonHelper.parse(json);
final JsonObject objectElement = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(objectElement);
validateChartSlabsUpdate(element, baseDataValidator, locale);
final Boolean isPrimaryGroupingByAmount = null;
validateChartSlabsUpdate(element, baseDataValidator, locale, isPrimaryGroupingByAmount);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateChartSlabsUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale) {
public void validateChartSlabsUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator, final Locale locale,
final Boolean isPrimaryGroupingByAmount) {
if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) {
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
@@ -166,8 +183,12 @@ public class InterestRateChartSlabDataValidator {
if (this.fromApiJsonHelper.parameterExists(periodTypeParamName, element)) {
final Integer periodType = this.fromApiJsonHelper.extractIntegerNamed(periodTypeParamName, element, locale);
baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull()
baseDataValidator.reset().parameter(periodTypeParamName).value(periodType)
.isOneOfTheseValues(PeriodFrequencyType.integerValues());
if (isPrimaryGroupingByAmount != null && !isPrimaryGroupingByAmount) {
baseDataValidator.reset().parameter(periodTypeParamName).value(periodType).notNull();
}
}
Integer fromPeriod = null;
@@ -175,12 +196,15 @@ public class InterestRateChartSlabDataValidator {
if (this.fromApiJsonHelper.parameterExists(fromPeriodParamName, element)) {
fromPeriod = this.fromApiJsonHelper.extractIntegerNamed(fromPeriodParamName, element, locale);
baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull().integerGreaterThanNumber(-1);
baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).integerZeroOrGreater();
if (isPrimaryGroupingByAmount != null && !isPrimaryGroupingByAmount) {
baseDataValidator.reset().parameter(fromPeriodParamName).value(fromPeriod).notNull();
}
}
if (this.fromApiJsonHelper.parameterExists(toPeriodParamName, element)) {
toPeriod = this.fromApiJsonHelper.extractIntegerNamed(toPeriodParamName, element, locale);
baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).notNull().integerGreaterThanNumber(-1);
baseDataValidator.reset().parameter(toPeriodParamName).value(toPeriod).integerGreaterThanZero();
}
if (fromPeriod != null && toPeriod != null) {
@@ -192,12 +216,15 @@ public class InterestRateChartSlabDataValidator {
BigDecimal amountRangeTo = null;
if (this.fromApiJsonHelper.parameterExists(amountRangeFromParamName, element)) {
amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale);
baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull().positiveAmount();
baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).zeroOrPositiveAmount();
if (isPrimaryGroupingByAmount != null && isPrimaryGroupingByAmount) {
baseDataValidator.reset().parameter(amountRangeFromParamName).value(amountRangeFrom).notNull();
}
}
if (this.fromApiJsonHelper.parameterExists(amountRangeToParamName, element)) {
amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale);
baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).notNull().positiveAmount();
baseDataValidator.reset().parameter(amountRangeToParamName).value(amountRangeTo).positiveAmount();
}
if (amountRangeFrom != null && amountRangeTo != null) {
@@ -51,6 +51,7 @@ import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants;
import org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.joda.time.LocalDate;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -82,12 +83,11 @@ public class InterestRateChart extends AbstractPersistable<Long> {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(INTERESTRATE_CHART_RESOURCE_NAME);
this.chartFields = chartFields;
// validate before setting the other fields
this.validateChartSlabs(baseDataValidator);
this.throwExceptionIfValidationWarningsExist(dataValidationErrors);
this.chartFields = chartFields;
this.addChartSlabs(interestRateChartSlabs);
}
@@ -97,22 +97,85 @@ public class InterestRateChart extends AbstractPersistable<Long> {
Integer tmpPeriodType = null;
List<InterestRateChartSlab> chartSlabsList = new ArrayList<>(chartSlabs);
boolean isPrimaryGroupingByAmount = this.chartFields.isPrimaryGroupingByAmount();
chartSlabsList.sort(new InterestRateChartSlabComparator<InterestRateChartSlab>(isPrimaryGroupingByAmount));
boolean isPeriodChart = !isPrimaryGroupingByAmount;
boolean isAmountChart = isPrimaryGroupingByAmount;
for (int i = 0; i < chartSlabsList.size(); i++) {
InterestRateChartSlab iSlabs = chartSlabsList.get(i);
if (tmpPeriodType == null) {
if (!iSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount)) {
if (isPrimaryGroupingByAmount) {
baseDataValidator.parameter(InterestRateChartSlabApiConstants.amountRangeFromParamName).failWithCode("cannot.be.blank");
} else {
baseDataValidator.parameter(InterestRateChartSlabApiConstants.fromPeriodParamName).failWithCode("cannot.be.blank");
}
} else if (i > 0) {
if (isPeriodChart ^ iSlabs.slabFields().fromPeriod() != null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.incomplete");
isPeriodChart = isPeriodChart || iSlabs.slabFields().fromPeriod() != null;
}
if (isAmountChart ^ iSlabs.slabFields().getAmountRangeFrom() != null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.incomplete");
isAmountChart = isAmountChart || iSlabs.slabFields().getAmountRangeFrom() != null;
}
}
if (i == 0) {
tmpPeriodType = iSlabs.slabFields().periodType();
} else if (!iSlabs.slabFields().periodType().equals(tmpPeriodType)) {
if (iSlabs.slabFields().isNotProperChartStart()) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.start.incorrect", iSlabs.slabFields()
.fromPeriod(), iSlabs.slabFields().getAmountRangeFrom());
}
isAmountChart = isAmountChart || iSlabs.slabFields().getAmountRangeFrom() != null;
isPeriodChart = isPeriodChart || iSlabs.slabFields().fromPeriod() != null;
} else if (iSlabs.slabFields().periodType() != null && !iSlabs.slabFields().periodType().equals(tmpPeriodType)) {
baseDataValidator.parameter(periodTypeParamName).value(iSlabs.slabFields().periodType())
.failWithCode("period.type.is.not.same", tmpPeriodType);
}
for (int j = i + 1; j < chartSlabsList.size(); j++) {
InterestRateChartSlab jSlabs = chartSlabsList.get(j);
if (iSlabs.slabFields().isPeriodOverlapping(jSlabs.slabFields())) {
baseDataValidator
.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.overlapping", iSlabs.slabFields().fromPeriod(),
iSlabs.slabFields().toPeriod(), jSlabs.slabFields().fromPeriod(), jSlabs.slabFields().toPeriod());
if (i + 1 < chartSlabsList.size()) {
InterestRateChartSlab nextSlabs = chartSlabsList.get(i + 1);
if (iSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount)
&& nextSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount)) {
if (iSlabs.slabFields().isRateChartOverlapping(nextSlabs.slabFields(), isPrimaryGroupingByAmount)) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.overlapping", iSlabs.slabFields()
.fromPeriod(), iSlabs.slabFields().toPeriod(), nextSlabs.slabFields().fromPeriod(), nextSlabs.slabFields()
.toPeriod(), iSlabs.slabFields().getAmountRangeFrom(), iSlabs.slabFields().getAmountRangeTo(), nextSlabs
.slabFields().getAmountRangeFrom(), nextSlabs.slabFields().getAmountRangeTo());
} else if (iSlabs.slabFields().isRateChartHasGap(nextSlabs.slabFields(), isPrimaryGroupingByAmount)) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.has.gap", iSlabs.slabFields()
.fromPeriod(), iSlabs.slabFields().toPeriod(), nextSlabs.slabFields().fromPeriod(), nextSlabs.slabFields()
.toPeriod(), iSlabs.slabFields().getAmountRangeFrom(), iSlabs.slabFields().getAmountRangeTo(), nextSlabs
.slabFields().getAmountRangeFrom(), nextSlabs.slabFields().getAmountRangeTo());
}
if (isPrimaryGroupingByAmount) {
if (!iSlabs.slabFields().isAmountSame(nextSlabs.slabFields())) {
if (InterestRateChartSlabFields.isNotProperPeriodStart(nextSlabs.slabFields())) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.start.incorrect",
nextSlabs.slabFields().toPeriod());
}
if (iSlabs.slabFields().toPeriod() != null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.end.incorrect", iSlabs
.slabFields().toPeriod());
}
}
} else if (!iSlabs.slabFields().isPeriodsSame(nextSlabs.slabFields())) {
if (InterestRateChartSlabFields.isNotProperAmountStart(nextSlabs.slabFields())) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.start.incorrect", nextSlabs
.slabFields().getAmountRangeFrom());
}
if (iSlabs.slabFields().getAmountRangeTo() != null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.end.incorrect", iSlabs
.slabFields().getAmountRangeTo());
}
}
}
} else if (iSlabs.slabFields().isNotProperPriodEnd()) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.end.incorrect",
iSlabs.slabFields().toPeriod(), iSlabs.slabFields().getAmountRangeTo());
}
}
}
@@ -24,6 +24,7 @@ import static org.apache.fineract.portfolio.interestratechart.InterestRateChartA
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.localeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.isPrimaryGroupingByAmountParamName;
import java.util.Date;
import java.util.Map;
@@ -56,19 +57,24 @@ public class InterestRateChartFields {
@Column(name = "end_date", nullable = false)
private Date endDate;
@Column(name = "is_primary_grouping_by_amount", nullable = false)
private boolean isPrimaryGroupingByAmount;
protected InterestRateChartFields() {
//
}
public static InterestRateChartFields createNew(String name, String description, LocalDate fromDate, LocalDate toDate) {
return new InterestRateChartFields(name, description, fromDate, toDate);
public static InterestRateChartFields createNew(String name, String description, LocalDate fromDate, LocalDate toDate,
boolean isPrimaryGroupingByAmount) {
return new InterestRateChartFields(name, description, fromDate, toDate, isPrimaryGroupingByAmount);
}
private InterestRateChartFields(String name, String description, LocalDate fromDate, LocalDate toDate) {
private InterestRateChartFields(String name, String description, LocalDate fromDate, LocalDate toDate, boolean isPrimaryGroupingByAmount) {
this.name = name;
this.description = description;
this.fromDate = fromDate.toDate();
this.endDate = (toDate == null) ? null : toDate.toDate();
this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount;
}
public void update(JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator) {
@@ -106,6 +112,12 @@ public class InterestRateChartFields {
this.endDate = newValue.toDate();
}
if (command.isChangeInBooleanParameterNamed(isPrimaryGroupingByAmountParamName, this.isPrimaryGroupingByAmount)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isPrimaryGroupingByAmountParamName);
actualChanges.put(isPrimaryGroupingByAmountParamName, newValue);
this.isPrimaryGroupingByAmount = newValue;
}
if (isFromDateAfterToDate()) {
baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date");
}
@@ -144,20 +156,22 @@ public class InterestRateChartFields {
final LocalDate thatFromDate = that.getFromDateAsLocalDate();
LocalDate thatEndDate = that.getEndDateAsLocalDate();
thatEndDate = thatEndDate == null ? DateUtils.getLocalDateOfTenant() : thatEndDate;
final LocalDateInterval thisInterval = LocalDateInterval.create(thisFromDate, thisEndDate);
final LocalDateInterval thatInterval = LocalDateInterval.create(thatFromDate, thatEndDate);
if(thisInterval.containsPortionOf(thatInterval) || thatInterval.containsPortionOf(thisInterval)){
return true;
}
if (thisInterval.containsPortionOf(thatInterval) || thatInterval.containsPortionOf(thisInterval)) { return true; }
return false;// no overlapping
}
public boolean isApplicableChartFor(final LocalDate target){
public boolean isApplicableChartFor(final LocalDate target) {
final LocalDate endDate = this.endDate == null ? DateUtils.getLocalDateOfTenant() : this.getEndDateAsLocalDate();
final LocalDateInterval interval = LocalDateInterval.create(getFromDateAsLocalDate(), endDate);
return interval.contains(target);
}
public boolean isPrimaryGroupingByAmount() {
return this.isPrimaryGroupingByAmount;
}
}
@@ -0,0 +1,92 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.fineract.portfolio.interestratechart.domain;
import java.math.BigDecimal;
import java.util.Comparator;
import org.apache.fineract.portfolio.savings.domain.DepositAccountInterestRateChartSlabs;
/**
* Sort InterestRateChartSlab by input range
*/
public class InterestRateChartSlabComparator<T> implements Comparator<T> {
private final boolean isPrimaryGroupingByAmount;
public InterestRateChartSlabComparator(final boolean isPrimaryGroupingByAmount) {
this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount;
}
@Override
public int compare(final T o1, final T o2) {
int compareResult = 0;
InterestRateChartSlabFields slabs1 = null;
InterestRateChartSlabFields slabs2 = null;
if (o1 instanceof InterestRateChartSlab) {
slabs1 = ((InterestRateChartSlab) o1).slabFields();
slabs2 = ((InterestRateChartSlab) o2).slabFields();
} else if (o1 instanceof DepositAccountInterestRateChartSlabs) {
slabs1 = ((DepositAccountInterestRateChartSlabs) o1).slabFields();
slabs2 = ((DepositAccountInterestRateChartSlabs) o2).slabFields();
} else {
return compareResult;
}
if (slabs1.isPeriodsSame(slabs2) && slabs1.isAmountSame(slabs2)) {
compareResult = 0;
} else {
if (isPrimaryGroupingByAmount) {
if (slabs1.isAmountSame(slabs2)) {
compareResult = comparePeriods(slabs1, slabs2);
} else {
compareResult = compareAmounts(slabs1, slabs2);
}
} else {
if (slabs1.isPeriodsSame(slabs2)) {
compareResult = compareAmounts(slabs1, slabs2);
} else {
compareResult = comparePeriods(slabs1, slabs2);
}
}
}
return compareResult;
}
private int comparePeriods(final InterestRateChartSlabFields slabs1, InterestRateChartSlabFields slabs2) {
int compareResult = 0;
Integer periodFrom1 = slabs1.fromPeriod();
Integer periodFrom2 = slabs2.fromPeriod();
if (periodFrom1 != null && periodFrom2 != null) {
compareResult = periodFrom1.compareTo(periodFrom2);
}
return compareResult;
}
private int compareAmounts(final InterestRateChartSlabFields slabs1, InterestRateChartSlabFields slabs2) {
int compareResult = 0;
BigDecimal amountFrom1 = slabs1.getAmountRangeFrom();
BigDecimal amountFrom2 = slabs2.getAmountRangeFrom();
if (amountFrom1 != null && amountFrom2 != null) {
compareResult = amountFrom1.compareTo(amountFrom2);
}
return compareResult;
}
}
@@ -51,15 +51,23 @@ public class InterestRateChartSlabFields {
@Column(name = "period_type_enum", nullable = false)
private Integer periodType;
@Column(name = "from_period", nullable = false)
@Column(name = "from_period")
private Integer fromPeriod;
@Column(name = "to_period", nullable = true)
@Column(name = "to_period")
private Integer toPeriod;
@Column(name = "amount_range_from", scale = 6, precision = 19)
private BigDecimal amountRangeFrom;
public BigDecimal getAmountRangeFrom() {
return this.amountRangeFrom;
}
public BigDecimal getAmountRangeTo() {
return this.amountRangeTo;
}
@Column(name = "amount_range_to", scale = 6, precision = 19)
private BigDecimal amountRangeTo;
@@ -84,7 +92,7 @@ public class InterestRateChartSlabFields {
final Integer fromPeriod, final Integer toPeriod, final BigDecimal amountRangeFrom, final BigDecimal amountRangeTo,
final BigDecimal annualInterestRate, final String currencyCode) {
this.description = description;
this.periodType = (periodFrequencyType == null) ? null : periodFrequencyType.getValue();
this.periodType = (periodFrequencyType == null || periodFrequencyType.isInvalid()) ? null : periodFrequencyType.getValue();
this.fromPeriod = fromPeriod;
this.toPeriod = toPeriod;
this.amountRangeFrom = amountRangeFrom;
@@ -183,17 +191,129 @@ public class InterestRateChartSlabFields {
return this.toPeriod;
}
public boolean isPeriodOverlapping(final InterestRateChartSlabFields that) {
if (that.toPeriod == null) {
if (this.toPeriod == null) { return true; }
return that.fromPeriod <= this.toPeriod;
public boolean isRateChartHasGap(final InterestRateChartSlabFields that, final boolean isPrimaryGroupingByAmount) {
boolean isPeriodSame = isPeriodsSame(that);
boolean isAmountSame = isAmountSame(that);
boolean hasPeriods = this.fromPeriod != null || that.fromPeriod != null;
boolean hasAmounts = this.amountRangeFrom != null || that.amountRangeFrom != null;
if (isPrimaryGroupingByAmount) {
if (isAmountSame) {
if (hasPeriods) {
if (this.toPeriod == null) { return true; }
return isNotProperPeriodStart(that.fromPeriod);
}
} else {
return isNotProperAmountStart(that.amountRangeFrom) || isNotProperPeriodStart(that);
}
} else {
if (isPeriodSame) {
if (hasAmounts) {
if (this.amountRangeTo == null) { return true; }
return isNotProperAmountStart(that.amountRangeFrom);
}
} else {
return isNotProperPeriodStart(that.fromPeriod) || isNotProperAmountStart(that);
}
}
return false;
}
public boolean isValidChart(boolean isPrimaryGroupingByAmount) {
return (!isPrimaryGroupingByAmount && this.fromPeriod != null) || (isPrimaryGroupingByAmount && this.amountRangeFrom != null);
}
public boolean isNotProperChartStart() {
return isNotProperPeriodStart(this) || isNotProperAmountStart(this);
}
public static boolean isNotProperAmountStart(final InterestRateChartSlabFields interestRateChartSlabFields) {
return interestRateChartSlabFields.amountRangeFrom != null
&& (interestRateChartSlabFields.amountRangeFrom.compareTo(BigDecimal.ONE) != 0 && interestRateChartSlabFields.amountRangeFrom
.compareTo(BigDecimal.ZERO) != 0);
}
private boolean isNotProperAmountStart(final BigDecimal amount) {
return this.amountRangeTo == null || (amount != null && amount.compareTo(this.amountRangeTo.add(BigDecimal.ONE)) != 0);
}
private boolean isNotProperPeriodStart(final Integer period) {
return this.toPeriod == null || (period != null && period.compareTo(this.toPeriod + 1) != 0);
}
public static boolean isNotProperPeriodStart(InterestRateChartSlabFields interestRateChartSlabFields) {
return interestRateChartSlabFields.fromPeriod != null
&& !(interestRateChartSlabFields.fromPeriod.equals(1) || interestRateChartSlabFields.fromPeriod.equals(0));
}
public boolean isNotProperPriodEnd() {
return !(this.toPeriod == null && this.amountRangeTo == null);
}
public boolean isRateChartOverlapping(final InterestRateChartSlabFields that, final boolean isPrimaryGroupingByAmount) {
boolean isPeriodOverLapping = isPeriodOverlapping(that);
boolean isAmountOverLapping = isAmountOverlapping(that);
boolean isPeriodSame = isPeriodsSame(that);
boolean isAmountSame = isAmountSame(that);
boolean isOverlapping = false;
if (isPrimaryGroupingByAmount) {
isOverlapping = (isAmountOverLapping && !isAmountSame) || (isPeriodOverLapping && isAmountSame);
} else {
isOverlapping = (isPeriodOverLapping && !isPeriodSame) || (isAmountOverLapping && isPeriodSame);
}
return isOverlapping;
}
private boolean isPeriodOverlapping(final InterestRateChartSlabFields that) {
if (isIntegerSame(that.toPeriod, this.toPeriod)) {
return true;
} else if (isIntegerSame(that.fromPeriod, this.fromPeriod)) {
return true;
} else if (this.toPeriod == null) {
return true;
} else if (that.toPeriod == null) { return that.fromPeriod <= this.toPeriod; }
return this.fromPeriod <= that.toPeriod && that.fromPeriod <= this.toPeriod;
}
private boolean isAmountOverlapping(final InterestRateChartSlabFields that) {
if (isBigDecimalSame(that.amountRangeFrom, this.amountRangeFrom)) {
return true;
} else if (isBigDecimalSame(that.amountRangeTo, this.amountRangeTo)) {
return true;
} else if (this.amountRangeTo == null) {
return true;
} else if (that.amountRangeTo == null) { return that.amountRangeFrom.compareTo(this.amountRangeTo) < 1; }
return this.amountRangeFrom.compareTo(that.amountRangeTo) < 1 && that.amountRangeFrom.compareTo(this.amountRangeTo) < 1;
}
public boolean isAmountSame(final InterestRateChartSlabFields that) {
return isBigDecimalSame(this.amountRangeFrom, that.amountRangeFrom) && isBigDecimalSame(this.amountRangeTo, that.amountRangeTo);
}
public boolean isPeriodsSame(final InterestRateChartSlabFields that) {
return isIntegerSame(this.fromPeriod, that.fromPeriod) && isIntegerSame(this.toPeriod, that.toPeriod);
}
public boolean isIntegerSame(final Integer obj1, final Integer obj2) {
if (obj1 == null || obj2 == null) {
if (obj1 == obj2) { return true; }
return false;
}
return obj1.equals(obj2);
}
public boolean isBigDecimalSame(final BigDecimal obj1, final BigDecimal obj2) {
if (obj1 == null || obj2 == null) {
if (obj1 == obj2) { return true; }
return false;
}
return obj1.compareTo(obj2) == 0;
}
public boolean isBetweenPeriod(final LocalDate periodStartDate, final LocalDate periodEndDate) {
final Integer compare = depositPeriod(periodStartDate, periodEndDate);
return (compare < this.fromPeriod || (this.toPeriod != null && compare > this.toPeriod)) ? false : true;
return isPeriodBetween(compare);
}
public boolean isAmountRangeProvided() {
@@ -233,8 +353,16 @@ public class InterestRateChartSlabFields {
returnValue = depositAmount.compareTo(amountRangeFrom) >= 0 && depositAmount.compareTo(amountRangeTo) <= 0;
} else if (amountRangeFrom != null) {
returnValue = depositAmount.compareTo(amountRangeFrom) >= 0;
} else if (amountRangeTo != null) {
returnValue = depositAmount.compareTo(amountRangeTo) <= 0;
}
return returnValue;
}
public boolean isPeriodBetween(final Integer periods) {
boolean returnValue = true;
if (fromPeriod != null && toPeriod != null) {
returnValue = periods.compareTo(fromPeriod) >= 0 && periods.compareTo(toPeriod) <= 0;
} else if (fromPeriod != null) {
returnValue = periods.compareTo(fromPeriod) >= 0;
}
return returnValue;
}
@@ -22,6 +22,7 @@ import static org.apache.fineract.portfolio.interestratechart.InterestRateChartA
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.isPrimaryGroupingByAmountParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.currencyCodeParamName;
@@ -65,33 +66,38 @@ public class InterestRateChartAssembler {
* request
*/
public InterestRateChart assembleFrom(final JsonCommand command) {
final JsonElement element = command.parsedJson();
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
@SuppressWarnings("unused")
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(INTERESTRATE_CHART_RESOURCE_NAME);
final JsonElement element = command.parsedJson();
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element);
final InterestRateChart newChart = this.assembleFrom(element, currencyCode);
final InterestRateChart newChart = this.assembleFrom(element, currencyCode, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
return newChart;
}
public InterestRateChart assembleFrom(final JsonElement element, final String currencyCode) {
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public InterestRateChart assembleFrom(final JsonElement element, final String currencyCode, final DataValidatorBuilder baseDataValidator) {
final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element);
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
final LocalDate fromDate = this.fromApiJsonHelper.extractLocalDateNamed(fromDateParamName, element);
final LocalDate toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element);
Boolean isPrimaryGroupingByAmount = this.fromApiJsonHelper.extractBooleanNamed(isPrimaryGroupingByAmountParamName, element);
if (isPrimaryGroupingByAmount == null) {
isPrimaryGroupingByAmount = false;
}
// assemble chart Slabs
final Collection<InterestRateChartSlab> newChartSlabs = this.chartSlabAssembler.assembleChartSlabsFrom(element,
currencyCode);
final Collection<InterestRateChartSlab> newChartSlabs = this.chartSlabAssembler.assembleChartSlabsFrom(element, currencyCode);
final InterestRateChartFields fields = InterestRateChartFields.createNew(name, description, fromDate, toDate);
final InterestRateChartFields fields = InterestRateChartFields.createNew(name, description, fromDate, toDate,
isPrimaryGroupingByAmount);
final InterestRateChart newChart = InterestRateChart.createNew(fields, newChartSlabs);
newChart.validateChartSlabs(baseDataValidator);
return newChart;
}
@@ -100,8 +106,4 @@ public class InterestRateChartAssembler {
.findOneWithNotFoundDetection(interestRateChartId);
return interestRateChart;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -86,8 +86,21 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha
@Override
public Collection<InterestRateChartData> retrieveAllWithSlabs(Long productId) {
this.context.authenticatedUser();
String sql = "select " + this.chartExtractor.schema() + " where sp.id = ? order by irc.id, ircd.id";
return this.jdbcTemplate.query(sql, this.chartExtractor, new Object[] { productId });
StringBuilder sql = new StringBuilder();
sql.append("select ");
sql.append(this.chartExtractor.schema());
sql.append(" where sp.id = ? order by irc.id, ");
sql.append("CASE ");
sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_from ");
sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_to ");
sql.append("END,");
sql.append("ircd.from_period, ircd.to_period,");
sql.append("CASE ");
sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_from ");
sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_to ");
sql.append("END");
return this.jdbcTemplate.query(sql.toString(), this.chartExtractor, new Object[] { productId });
}
@Override
@@ -177,6 +190,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha
sqlBuilder
.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription,")
.append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ")
.append("irc.is_primary_grouping_by_amount as isPrimaryGroupingByAmount, ")
.append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ")
.append("ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, ")
.append("ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, ")
@@ -239,6 +253,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha
sqlBuilder.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription, ")
.append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ")
.append("irc.is_primary_grouping_by_amount as isPrimaryGroupingByAmount, ")
.append("sp.id as savingsProductId, sp.name as savingsProductName ").append("from ")
.append("m_interest_rate_chart irc ")
.append("left join m_deposit_product_interest_rate_chart dpirc on irc.id=dpirc.interest_rate_chart_id ")
@@ -253,10 +268,11 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha
final String description = rs.getString("ircDescription");
final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "ircFromDate");
final LocalDate endDate = JdbcSupport.getLocalDate(rs, "ircEndDate");
final boolean isPrimaryGroupingByAmount = rs.getBoolean("isPrimaryGroupingByAmount");
final Long savingsProductId = JdbcSupport.getLongDefaultToNullIfZero(rs, "savingsProductId");
final String savingsProductName = rs.getString("savingsProductName");
return InterestRateChartData.instance(id, name, description, fromDate, endDate, savingsProductId, savingsProductName);
return InterestRateChartData.instance(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, savingsProductId, savingsProductName);
}
}
@@ -299,7 +315,10 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha
final Integer fromPeriod = JdbcSupport.getInteger(rs, "ircdFromPeriod");
final Integer toPeriod = JdbcSupport.getInteger(rs, "ircdToPeriod");
final Integer periodTypeId = JdbcSupport.getInteger(rs, "ircdPeriodTypeId");
final EnumOptionData periodType = InterestRateChartEnumerations.periodType(periodTypeId);
EnumOptionData periodType = null;
if (periodTypeId != null) {
periodType = InterestRateChartEnumerations.periodType(periodTypeId);
}
final BigDecimal amountRangeFrom = rs.getBigDecimal("ircdAmountRangeFrom");
final BigDecimal amountRangeTo = rs.getBigDecimal("ircdAmountRangeTo");
final BigDecimal annualInterestRate = rs.getBigDecimal("ircdAnnualInterestRate");
@@ -18,9 +18,8 @@
*/
package org.apache.fineract.portfolio.savings.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
@@ -38,10 +37,11 @@ public class DepositAccountInterestRateChartData {
private final String description;
private final LocalDate fromDate;
private final LocalDate endDate;
private final boolean isPrimaryGroupingByAmount;
private final Long accountId;
private final String accountNumber;
// associations
private Set<DepositAccountInterestRateChartSlabData> chartSlabs;
private Collection<DepositAccountInterestRateChartSlabData> chartSlabs;
// template
private final Collection<EnumOptionData> periodTypes;
@@ -54,8 +54,8 @@ public class DepositAccountInterestRateChartData {
private final Collection<CodeValueData> clientClassificationOptions;
public static DepositAccountInterestRateChartData instance(Long id, String name, String description, LocalDate fromDate,
LocalDate endDate, Long accountId, String accountNumber, Set<DepositAccountInterestRateChartSlabData> chartSlabs,
Collection<EnumOptionData> periodTypes) {
LocalDate endDate, boolean isPrimaryGroupingByAmount, Long accountId, String accountNumber,
Collection<DepositAccountInterestRateChartSlabData> chartSlabs, Collection<EnumOptionData> periodTypes) {
final Collection<EnumOptionData> entityTypeOptions = null;
final Collection<EnumOptionData> attributeNameOptions = null;
@@ -64,17 +64,17 @@ public class DepositAccountInterestRateChartData {
final Collection<CodeValueData> genderOptions = null;
final Collection<CodeValueData> clientTypeOptions = null;
final Collection<CodeValueData> clientClassificationOptions = null;
return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, accountId, accountNumber, chartSlabs,
periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions,
clientTypeOptions, clientClassificationOptions);
return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, accountId,
accountNumber, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static DepositAccountInterestRateChartData from(InterestRateChartData productChartData) {
final Long id = null;
final Long accountId = null;
final String accountNumber = null;
Set<DepositAccountInterestRateChartSlabData> fromProdChartSlabs = new HashSet<>();
Set<InterestRateChartSlabData> productChartSlabDatas = productChartData.chartSlabs();
Collection<DepositAccountInterestRateChartSlabData> fromProdChartSlabs = new ArrayList<>();
Collection<InterestRateChartSlabData> productChartSlabDatas = productChartData.chartSlabs();
if (productChartSlabDatas != null) {
for (InterestRateChartSlabData productChartSlabData : productChartSlabDatas) {
fromProdChartSlabs.add(DepositAccountInterestRateChartSlabData.from(productChartSlabData));
@@ -82,20 +82,20 @@ public class DepositAccountInterestRateChartData {
}
return new DepositAccountInterestRateChartData(id, productChartData.name(), productChartData.description(),
productChartData.fromDate(), productChartData.endDate(), accountId, accountNumber, fromProdChartSlabs,
productChartData.periodTypes(), productChartData.entityTypeOptions(), productChartData.attributeNameOptions(),
productChartData.conditionTypeOptions(), productChartData.incentiveTypeOptions(), productChartData.genderOptions(),
productChartData.clientTypeOptions(), productChartData.clientClassificationOptions());
productChartData.fromDate(), productChartData.endDate(), productChartData.isPrimaryGroupingByAmount(), accountId,
accountNumber, fromProdChartSlabs, productChartData.periodTypes(), productChartData.entityTypeOptions(),
productChartData.attributeNameOptions(), productChartData.conditionTypeOptions(), productChartData.incentiveTypeOptions(),
productChartData.genderOptions(), productChartData.clientTypeOptions(), productChartData.clientClassificationOptions());
}
public static DepositAccountInterestRateChartData withSlabs(DepositAccountInterestRateChartData interestRateChartData,
Set<DepositAccountInterestRateChartSlabData> chartSlabs) {
Collection<DepositAccountInterestRateChartSlabData> chartSlabs) {
return new DepositAccountInterestRateChartData(interestRateChartData.id, interestRateChartData.name,
interestRateChartData.description, interestRateChartData.fromDate, interestRateChartData.endDate,
interestRateChartData.accountId, interestRateChartData.accountNumber, chartSlabs, interestRateChartData.periodTypes,
interestRateChartData.entityTypeOptions, interestRateChartData.attributeNameOptions,
interestRateChartData.conditionTypeOptions, interestRateChartData.incentiveTypeOptions,
interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions,
interestRateChartData.isPrimaryGroupingByAmount, interestRateChartData.accountId, interestRateChartData.accountNumber,
chartSlabs, interestRateChartData.periodTypes, interestRateChartData.entityTypeOptions,
interestRateChartData.attributeNameOptions, interestRateChartData.conditionTypeOptions,
interestRateChartData.incentiveTypeOptions, interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions,
interestRateChartData.clientClassificationOptions);
}
@@ -106,9 +106,9 @@ public class DepositAccountInterestRateChartData {
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions) {
return new DepositAccountInterestRateChartData(interestRateChartData.id, interestRateChartData.name,
interestRateChartData.description, interestRateChartData.fromDate, interestRateChartData.endDate,
interestRateChartData.accountId, interestRateChartData.accountNumber, interestRateChartData.chartSlabs, periodTypes,
entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, clientTypeOptions,
clientClassificationOptions);
interestRateChartData.isPrimaryGroupingByAmount, interestRateChartData.accountId, interestRateChartData.accountNumber,
interestRateChartData.chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static DepositAccountInterestRateChartData template(Collection<EnumOptionData> periodTypes,
@@ -123,23 +123,26 @@ public class DepositAccountInterestRateChartData {
final LocalDate endDate = null;
final Long accountId = null;
final String accountNumber = null;
final Set<DepositAccountInterestRateChartSlabData> chartSlabs = null;
return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, accountId, accountNumber, chartSlabs,
periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions,
clientTypeOptions, clientClassificationOptions);
final boolean isPrimaryGroupingByAmount = false;
final Collection<DepositAccountInterestRateChartSlabData> chartSlabs = null;
return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, accountId,
accountNumber, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
private DepositAccountInterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate,
Long accountId, String accountNumber, Set<DepositAccountInterestRateChartSlabData> chartSlabs,
Collection<EnumOptionData> periodTypes, final Collection<EnumOptionData> entityTypeOptions,
final Collection<EnumOptionData> attributeNameOptions, final Collection<EnumOptionData> conditionTypeOptions,
final Collection<EnumOptionData> incentiveTypeOptions, final Collection<CodeValueData> genderOptions,
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions) {
final boolean isPrimaryGroupingByAmount, Long accountId, String accountNumber,
Collection<DepositAccountInterestRateChartSlabData> chartSlabs, Collection<EnumOptionData> periodTypes,
final Collection<EnumOptionData> entityTypeOptions, final Collection<EnumOptionData> attributeNameOptions,
final Collection<EnumOptionData> conditionTypeOptions, final Collection<EnumOptionData> incentiveTypeOptions,
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
this.id = id;
this.name = name;
this.description = description;
this.fromDate = fromDate;
this.endDate = endDate;
this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount;
this.chartSlabs = chartSlabs;
this.accountId = accountId;
this.accountNumber = accountNumber;
@@ -155,7 +158,7 @@ public class DepositAccountInterestRateChartData {
public void addChartSlab(final DepositAccountInterestRateChartSlabData chartSlab) {
if (this.chartSlabs == null) {
this.chartSlabs = new HashSet<>();
this.chartSlabs = new ArrayList<>();
}
this.chartSlabs.add(chartSlab);
@@ -35,16 +35,24 @@ import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumb
import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType;
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
import org.apache.fineract.portfolio.calendar.domain.CalendarType;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.client.domain.AccountNumberGenerator;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType;
import org.apache.fineract.portfolio.savings.DepositAccountType;
@@ -71,6 +79,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
private final ConfigurationDomainService configurationDomainService;
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
private final CalendarInstanceRepository calendarInstanceRepository;
@Autowired
public DepositAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository,
@@ -79,7 +88,8 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final DepositAccountAssembler depositAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final ConfigurationDomainService configurationDomainService,
final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository) {
final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository,
final CalendarInstanceRepository calendarInstanceRepository) {
this.savingsAccountRepository = savingsAccountRepository;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
@@ -89,6 +99,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
this.configurationDomainService = configurationDomainService;
this.accountNumberFormatRepository = accountNumberFormatRepository;
this.calendarInstanceRepository = calendarInstanceRepository;
}
@Transactional
@@ -129,6 +140,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
boolean isAccountTransfer = false;
final boolean isPreMatureClosure = false;
final MathContext mc = MathContext.DECIMAL64;
account.updateDepositAmount(transactionAmount);
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(account, fmt, transactionDate,
transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction);
@@ -241,18 +253,15 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
if (onClosureType.isReinvest()) {
RecurringDepositAccount reinvestedDeposit = account.reInvest(transactionAmount);
depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
reinvestedDeposit.processAccountUponActivation(fmt, user);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountRepository.save(reinvestedDeposit);
Money amountForDeposit = reinvestedDeposit.activateWithBalance();
if (amountForDeposit.isGreaterThanZero()) {
handleRDDeposit(reinvestedDeposit, fmt, reinvestedDeposit.getActivationLocalDate(), amountForDeposit.getAmount(),
paymentDetail, isRegularTransaction);
}
final CalendarInstance calendarInstance = getCalendarInstance(account, reinvestedDeposit);
this.calendarInstanceRepository.save(calendarInstance);
final Calendar calendar = calendarInstance.getCalendar();
final PeriodFrequencyType frequencyType = CalendarFrequencyType.from(CalendarUtils.getFrequency(calendar.getRecurrence()));
Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence());
frequency = frequency == -1 ? 1 : frequency;
reinvestedDeposit.generateSchedule(frequencyType, frequency, calendar);
reinvestedDeposit.processAccountUponActivation(fmt, user);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountRepository.save(reinvestedDeposit);
@@ -292,6 +301,31 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
return savingsTransactionId;
}
private CalendarInstance getCalendarInstance(final RecurringDepositAccount account, RecurringDepositAccount reinvestedDeposit) {
CalendarInstance calendarInstance = null;
CalendarInstance parentCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId(
account.getId(), CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue());
if (account.isCalendarInherited()) {
calendarInstance = CalendarInstance.from(parentCalendarInstance.getCalendar(), account.getId(),
CalendarEntityType.SAVINGS.getValue());
} else {
LocalDate calendarStartDate = reinvestedDeposit.depositStartDate();
Calendar parentCalendar = parentCalendarInstance.getCalendar();
final String recurrence = parentCalendar.getRecurrence();
final String title = "recurring_savings_" + reinvestedDeposit.getId();
final Calendar calendar = Calendar.createRepeatingCalendar(title, calendarStartDate, CalendarType.COLLECTION.getValue(),
recurrence);
calendarInstance = CalendarInstance.from(calendar, reinvestedDeposit.getId(), CalendarEntityType.SAVINGS.getValue());
}
if (calendarInstance == null) {
final String defaultUserMessage = "No valid recurring details available for recurring depost account creation.";
throw new GeneralPlatformDomainRuleException(
"error.msg.recurring.deposit.account.cannot.create.no.valid.recurring.details.available", defaultUserMessage,
account.clientId());
}
return calendarInstance;
}
private void autoGenerateAccountNumber(final SavingsAccount account) {
if (account.isAccountNumberRequiresAutoGeneration()) {
final AccountNumberFormat accountNumberFormat = this.accountNumberFormatRepository.findByAccountType(EntityAccountType.SAVINGS);
@@ -92,6 +92,7 @@ public class DepositAccountInterestRateChart extends AbstractPersistable<Long> {
if (this.chartSlabs == null) {
this.chartSlabs = new HashSet<>();
}
return this.chartSlabs;
}
@@ -153,4 +154,8 @@ public class DepositAccountInterestRateChart extends AbstractPersistable<Long> {
return effectiveInterestRate;
}
public boolean isPrimaryGroupingByAmount() {
return this.chartFields.isPrimaryGroupingByAmount();
}
}
@@ -18,6 +18,8 @@
*/
package org.apache.fineract.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.adjustAdvanceTowardsFuturePaymentsParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.allowWithdrawalParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.chartsParamName;
@@ -52,11 +54,16 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalA
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
@@ -147,7 +154,11 @@ public class DepositProductAssembler {
// Savings product charges
final Set<Charge> charges = assembleListOfSavingsProductCharges(command, currencyCode);
// Interest rate charts
final Set<InterestRateChart> charts = assembleListOfCharts(command, currency.getCode());
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
final Set<InterestRateChart> charts = assembleListOfCharts(command, currency.getCode(), baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
if (interestRate == null) {
interestRate = BigDecimal.ZERO;
}
@@ -164,6 +175,10 @@ public class DepositProductAssembler {
return fixedDepositProduct;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public RecurringDepositProduct assembleRecurringDepositProduct(final JsonCommand command) {
final String name = command.stringValueOfParameterNamed(nameParamName);
@@ -225,7 +240,11 @@ public class DepositProductAssembler {
// Savings product charges
final Set<Charge> charges = assembleListOfSavingsProductCharges(command, currencyCode);
// Interest rate charts
final Set<InterestRateChart> charts = assembleListOfCharts(command, currency.getCode());
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
final Set<InterestRateChart> charts = assembleListOfCharts(command, currency.getCode(), baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
if (interestRate == null) {
interestRate = BigDecimal.ZERO;
@@ -435,15 +454,14 @@ public class DepositProductAssembler {
return charges;
}
private Set<InterestRateChart> assembleListOfCharts(JsonCommand command, String currencyCode) {
private Set<InterestRateChart> assembleListOfCharts(JsonCommand command, String currencyCode, DataValidatorBuilder baseDataValidator) {
final Set<InterestRateChart> charts = new HashSet<>();
if (command.parameterExists(chartsParamName)) {
final JsonArray chartsArray = command.arrayOfParameterNamed(chartsParamName);
if (chartsArray != null) {
for (int i = 0; i < chartsArray.size(); i++) {
final JsonObject interstRateChartElement = chartsArray.get(i).getAsJsonObject();
InterestRateChart chart = this.chartAssembler.assembleFrom(interstRateChartElement, currencyCode);
InterestRateChart chart = this.chartAssembler.assembleFrom(interstRateChartElement, currencyCode, baseDataValidator);
charts.add(chart);
}
}
@@ -198,7 +198,7 @@ public class FixedDepositProduct extends SavingsProduct {
}
} else {
// assemble chart
final InterestRateChart newChart = this.chartAssembler.assembleFrom(chartElement, this.currency().getCode());
final InterestRateChart newChart = this.chartAssembler.assembleFrom(chartElement, this.currency().getCode(), baseDataValidator);
this.addChart(newChart);
}
}
@@ -174,12 +174,29 @@ public class RecurringDepositAccount extends SavingsAccount {
final Map<String, Object> termAndPreClosureChanges = accountTermAndPreClosure.update(command, baseDataValidator);
actualChanges.putAll(termAndPreClosureChanges);
recurringDetail.update(command);
validateDomainRules(baseDataValidator);
super.validateInterestPostingAndCompoundingPeriodTypes(baseDataValidator);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
private void updateDepositAmount() {
BigDecimal recurringAmount = getRecurringDetail().mandatoryRecommendedDepositAmount();
Integer numberOfDepositPeriods = depositScheduleInstallments().size();
if (this.accountTermAndPreClosure.depositPeriod() != null && recurringAmount != null && numberOfDepositPeriods != null) {
BigDecimal depositAmount = Money.of(product.currency(), recurringAmount).multipliedBy(numberOfDepositPeriods)
.plus(this.minRequiredOpeningBalance).getAmount();
accountTermAndPreClosure.updateDepositAmount(depositAmount);
} else if (accountTermAndPreClosure.depositAmount() == null) {
accountTermAndPreClosure.updateDepositAmount(Money.zero(product.currency()).getAmount());
}
}
public void updateDepositAmount(final BigDecimal depositAmount) {
if (this.accountTermAndPreClosure.depositPeriod() == null) {
accountTermAndPreClosure.updateDepositAmount(accountTermAndPreClosure.depositAmount().add(depositAmount));
}
}
@Override
protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate interestPostingUpToDate) {
boolean isPreMatureClosure = false;
@@ -207,7 +224,7 @@ public class RecurringDepositAccount extends SavingsAccount {
}
if (depositCloseDate == null) {
depositCloseDate = LocalDate.now();
depositCloseDate = DateUtils.getLocalDateOfTenant();
}
final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount();
@@ -966,8 +983,8 @@ public class RecurringDepositAccount extends SavingsAccount {
.failWithCode("deposit.period.must.be.greater.than.lock.in.period",
"Deposit period must be greater than account lock-in period.");
}
}
}
if (firstDepositDateBeforeAccountSubmittedOrActivationDate()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
"expected.first.deposit.date.must.be.after.account.submitted.or.activation.date");
@@ -989,7 +1006,7 @@ public class RecurringDepositAccount extends SavingsAccount {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
LocalDate maturityDate = calculateMaturityDate();
if (this.chart != null && maturityDate != null) {
if (this.chart != null) {
final LocalDate chartFromDate = this.chart.getFromDateAsLocalDate();
LocalDate chartEndDate = this.chart.getEndDateAsLocalDate();
chartEndDate = chartEndDate == null ? DateUtils.getLocalDateOfTenant() : chartEndDate;
@@ -999,7 +1016,11 @@ public class RecurringDepositAccount extends SavingsAccount {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.valid.interest.rate.slab.available.for.date.range");
}
final BigDecimal maturityAmount = this.accountTermAndPreClosure.maturityAmount();
if (maturityDate == null) {
maturityDate = DateUtils.getLocalDateOfTenant();
}
final BigDecimal maturityAmount = this.accountTermAndPreClosure.depositAmount();
BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(maturityAmount, depositStartDate(), maturityDate,
this.client);
@@ -1012,7 +1033,7 @@ public class RecurringDepositAccount extends SavingsAccount {
baseDataValidator.reset().parameter(DepositsApiConstants.nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate)
.failWithCodeNoParameterAddedToErrorCode("interest.chart.or.nominal.interest.rate.required");
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
/**
* final boolean recurringFrequencyBeforeDepositPeriod =
* recurringFrequencyBeforeDepositPeriod();
@@ -1117,6 +1138,7 @@ public class RecurringDepositAccount extends SavingsAccount {
installmentDate = DepositAccountUtils.calculateNextDepositDate(installmentDate, frequency, recurringEvery);
installmentNumber += 1;
}
updateDepositAmount();
}
private LocalDate calcualteScheduleTillDate(final PeriodFrequencyType frequency, final Integer recurringEvery) {
@@ -90,8 +90,20 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D
@Override
public DepositAccountInterestRateChartData retrieveOneWithSlabs(Long chartId) {
this.context.authenticatedUser();
final String sql = "select " + this.chartExtractor.schema() + " where irc.id = ? order by ircd.id asc";
Collection<DepositAccountInterestRateChartData> chartDatas = this.jdbcTemplate.query(sql, this.chartExtractor,
StringBuilder sql = new StringBuilder();
sql.append("select ");
sql.append(this.chartExtractor.schema());
sql.append(" where irc.id = ? order by irc.id asc, ");
sql.append("CASE ");
sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_from ");
sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_to ");
sql.append("END,");
sql.append("ircd.from_period, ircd.to_period,");
sql.append("CASE ");
sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_from ");
sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_to ");
sql.append("END");
Collection<DepositAccountInterestRateChartData> chartDatas = this.jdbcTemplate.query(sql.toString(), this.chartExtractor,
new Object[] { chartId });
if (chartDatas == null || chartDatas.isEmpty()) { throw new DepositAccountInterestRateChartNotFoundException(chartId); }
@@ -122,8 +134,21 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D
@Override
public DepositAccountInterestRateChartData retrieveOneWithSlabsOnAccountId(Long accountId) {
this.context.authenticatedUser();
final String sql = "select " + this.chartExtractor.schema() + " where irc.savings_account_id = ? order by ircd.id asc";
Collection<DepositAccountInterestRateChartData> chartDatas = this.jdbcTemplate.query(sql, this.chartExtractor,
StringBuilder sql = new StringBuilder();
sql.append("select ");
sql.append(this.chartExtractor.schema());
sql.append(" where irc.savings_account_id = ? order by irc.id asc, ");
sql.append("CASE ");
sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_from ");
sql.append("WHEN isPrimaryGroupingByAmount then ircd.amount_range_to ");
sql.append("END,");
sql.append("ircd.from_period, ircd.to_period,");
sql.append("CASE ");
sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_from ");
sql.append("WHEN !isPrimaryGroupingByAmount then ircd.amount_range_to ");
sql.append("END");
Collection<DepositAccountInterestRateChartData> chartDatas = this.jdbcTemplate.query(sql.toString(), this.chartExtractor,
new Object[] { accountId });
if (chartDatas == null || chartDatas.isEmpty()) { throw new DepositAccountInterestRateChartNotFoundException(accountId); }
@@ -167,6 +192,7 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D
sqlBuilder
.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription,")
.append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ")
.append("irc.is_primary_grouping_by_amount as isPrimaryGroupingByAmount,")
.append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ")
.append("ircd.from_period as ircdFromPeriod, ircd.to_period as ircdToPeriod, ircd.amount_range_from as ircdAmountRangeFrom, ")
.append("ircd.amount_range_to as ircdAmountRangeTo, ircd.annual_interest_rate as ircdAnnualInterestRate, ")
@@ -231,6 +257,7 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D
sqlBuilder.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription, ")
.append("irc.from_date as ircFromDate, irc.end_date as ircEndDate, ")
.append("irc.is_primary_grouping_by_amount as isPrimaryGroupingByAmount,")
.append("sa.id as accountId, sa.account_no as accountNumber ").append("from ")
.append("m_savings_account_interest_rate_chart irc left join m_savings_account sa on irc.savings_account_id=sa.id ");
this.schemaSql = sqlBuilder.toString();
@@ -243,12 +270,13 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D
final String description = rs.getString("ircDescription");
final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "ircFromDate");
final LocalDate endDate = JdbcSupport.getLocalDate(rs, "ircEndDate");
final boolean isPrimaryGroupingByAmount = rs.getBoolean("isPrimaryGroupingByAmount");
final Long accountId = rs.getLong("accountId");
final String accountNumber = rs.getString("accountNumber");
final Collection<EnumOptionData> periodTypes = InterestRateChartEnumerations.periodType(PeriodFrequencyType.values());
return DepositAccountInterestRateChartData.instance(id, name, description, fromDate, endDate, accountId, accountNumber, null,
periodTypes);
return DepositAccountInterestRateChartData.instance(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount,
accountId, accountNumber, null, periodTypes);
}
}
@@ -0,0 +1,13 @@
ALTER TABLE `m_interest_rate_slab`
CHANGE COLUMN `from_period` `from_period` INT(11) NULL DEFAULT NULL,
CHANGE COLUMN `period_type_enum` `period_type_enum` SMALLINT(5) NULL DEFAULT NULL ;
ALTER TABLE `m_interest_rate_chart`
ADD COLUMN `is_primary_grouping_by_amount` TINYINT(1) NOT NULL DEFAULT '0';
ALTER TABLE `m_savings_account_interest_rate_chart`
ADD COLUMN `is_primary_grouping_by_amount` TINYINT NOT NULL DEFAULT '0' ;
ALTER TABLE `m_savings_account_interest_rate_slab`
CHANGE COLUMN `period_type_enum` `period_type_enum` SMALLINT(5) NULL DEFAULT NULL,
CHANGE COLUMN `from_period` `from_period` INT(11) NULL DEFAULT NULL;