FINERACT-2107: Interest refund configuration
This commit is contained in:
+2
-1
@@ -522,7 +522,8 @@ public class LoanScheduleAssembler {
|
||||
fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled,
|
||||
disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
|
||||
loanScheduleType, loanScheduleProcessingType, fixedLength,
|
||||
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
|
||||
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting(),
|
||||
loanProduct.getLoanProductRelatedDetail().getSupportedInterestRefundTypes());
|
||||
}
|
||||
|
||||
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
||||
|
||||
+5
-1
@@ -59,6 +59,7 @@ import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.data.StringEnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
@@ -84,6 +85,7 @@ import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrat
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.FutureInstallmentAllocationRule;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationTransactionType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
@@ -421,6 +423,8 @@ public class LoanProductsApiResource {
|
||||
final List<EnumOptionData> advancedPaymentAllocationTypes = PaymentAllocationType.getValuesAsEnumOptionDataList();
|
||||
final List<EnumOptionData> creditAllocationTransactionTypes = CreditAllocationTransactionType.getValuesAsEnumOptionDataList();
|
||||
final List<EnumOptionData> creditAllocationAllocationTypes = AllocationType.getValuesAsEnumOptionDataList();
|
||||
final List<StringEnumOptionData> supportedInterestRefundTypesOptions = LoanSupportedInterestRefundTypes
|
||||
.getValuesAsStringEnumOptionDataList();
|
||||
|
||||
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions,
|
||||
interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
|
||||
@@ -432,7 +436,7 @@ public class LoanProductsApiResource {
|
||||
advancedPaymentAllocationTransactionTypes, advancedPaymentAllocationFutureInstallmentAllocationRules,
|
||||
advancedPaymentAllocationTypes, LoanScheduleType.getValuesAsEnumOptionDataList(),
|
||||
LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), creditAllocationTransactionTypes,
|
||||
creditAllocationAllocationTypes);
|
||||
creditAllocationAllocationTypes, supportedInterestRefundTypesOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-1
@@ -61,6 +61,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCa
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductPaymentAllocationRule;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.EqualAmortizationUnsupportedFeatureException;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -175,7 +176,7 @@ public final class LoanProductDataValidator {
|
||||
LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.REPAYMENT_START_DATE_TYPE,
|
||||
LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.LOAN_SCHEDULE_TYPE,
|
||||
LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH,
|
||||
LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING));
|
||||
LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES));
|
||||
|
||||
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
|
||||
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
|
||||
@@ -841,6 +842,19 @@ public final class LoanProductDataValidator {
|
||||
validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||
graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator);
|
||||
|
||||
if (AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY.equals(transactionProcessingStrategyCode)
|
||||
&& this.fromApiJsonHelper.parameterExists(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES, element)) {
|
||||
String[] supportedTransactionsForInterestRefund = this.fromApiJsonHelper
|
||||
.extractArrayNamed(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES, element);
|
||||
Arrays.stream(supportedTransactionsForInterestRefund)
|
||||
.forEach(value -> baseDataValidator.reset().parameter(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES).value(value)
|
||||
.isOneOfEnumValues(LoanSupportedInterestRefundTypes.class));
|
||||
} else if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES, element)) {
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES).failWithCode(
|
||||
"supported.only.for.progressive.loan.schedule.handling",
|
||||
"Automatic interest refund functionality is only supported for Progressive loans");
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.data.StringEnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
@@ -56,6 +57,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductParamType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
import org.apache.fineract.portfolio.rate.service.RateReadService;
|
||||
@@ -276,7 +278,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "lfr.is_floating_interest_rate_calculation_allowed as isFloatingInterestRateCalculationAllowed, "
|
||||
+ "lp.allow_variabe_installments as isVariableIntallmentsAllowed, " + "lvi.minimum_gap as minimumGap, "
|
||||
+ "lvi.maximum_gap as maximumGap, dbuc.id as delinquencyBucketId, dbuc.name as delinquencyBucketName, "
|
||||
+ "lp.can_use_for_topup as canUseForTopup, lp.is_equal_amortization as isEqualAmortization, lp.loan_schedule_type as loanScheduleType, lp.loan_schedule_processing_type as loanScheduleProcessingType "
|
||||
+ "lp.can_use_for_topup as canUseForTopup, lp.is_equal_amortization as isEqualAmortization, lp.loan_schedule_type as loanScheduleType, lp.loan_schedule_processing_type as loanScheduleProcessingType, lp.supported_interest_refund_types as supportedInterestRefundTypes "
|
||||
+ " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id "
|
||||
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
|
||||
+ " left join m_product_loan_guarantee_details lpg on lpg.loan_product_id=lp.id "
|
||||
@@ -526,6 +528,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final String loanScheduleProcessingTypeStr = rs.getString("loanScheduleProcessingType");
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType = LoanScheduleProcessingType.valueOf(loanScheduleProcessingTypeStr);
|
||||
final boolean enableAccrualActivityPosting = rs.getBoolean("enableAccrualActivityPosting");
|
||||
final String supportedInterestRefundTypesString = rs.getString("supportedInterestRefundTypes");
|
||||
List<StringEnumOptionData> supportedInterestRefundTypes = List.of();
|
||||
if (supportedInterestRefundTypesString != null && !supportedInterestRefundTypesString.isEmpty()) {
|
||||
supportedInterestRefundTypes = Arrays.stream(supportedInterestRefundTypesString.split(","))
|
||||
.map(LoanSupportedInterestRefundTypes::valueOf)
|
||||
.map(LoanSupportedInterestRefundTypes::getValueAsStringEnumOptionData).toList();
|
||||
}
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -548,7 +557,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
enableAutoRepaymentForDownPayment, advancedPaymentData, creditAllocationData, repaymentStartDateType,
|
||||
enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(),
|
||||
fixedLength, enableAccrualActivityPosting);
|
||||
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -60,6 +60,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductCreditAllocationRule;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductPaymentAllocationRule;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductCannotBeModifiedDueToNonClosedLoansException;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductDateException;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
@@ -122,6 +123,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
}
|
||||
final LoanProduct loanProduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategyCode, charges, command,
|
||||
this.aprCalculator, floatingRate, rates, loanProductPaymentAllocationRules, loanProductCreditAllocationRules);
|
||||
|
||||
loanProduct.updateLoanProductInRelatedClasses();
|
||||
loanProduct.setTransactionProcessingStrategyName(
|
||||
loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(loanTransactionProcessingStrategyCode).getName());
|
||||
@@ -264,6 +266,16 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
}
|
||||
}
|
||||
|
||||
if (command.parameterExists(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES)) {
|
||||
JsonArray supportedTransactionsForInterestRefund = command
|
||||
.arrayOfParameterNamed(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES);
|
||||
List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes = new ArrayList<>();
|
||||
supportedTransactionsForInterestRefund.iterator().forEachRemaining(value -> {
|
||||
supportedInterestRefundTypes.add(LoanSupportedInterestRefundTypes.valueOf(value.getAsString()));
|
||||
});
|
||||
product.getLoanProductRelatedDetail().setSupportedInterestRefundTypes(supportedInterestRefundTypes);
|
||||
}
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
product.validateLoanProductPreSave();
|
||||
this.loanProductRepository.saveAndFlush(product);
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanCreditAllocationRule</class>
|
||||
<class>org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType</class>
|
||||
<class>org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationTypeListConverter</class>
|
||||
<class>org.apache.fineract.portfolio.loanproduct.domain.SupportedInterestRefundTypesListConverter</class>
|
||||
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
||||
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
||||
<class>org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks</class>
|
||||
|
||||
+2
-2
@@ -95,7 +95,7 @@ public class DefaultScheduledDateGeneratorTest {
|
||||
Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST, BigDecimal.valueOf(36_000L), null,
|
||||
DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO, null, NONE, null, ZERO,
|
||||
EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, false, DISBURSEMENT_DATE,
|
||||
submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false);
|
||||
submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null);
|
||||
|
||||
// when
|
||||
List<PreGeneratedLoanSchedulePeriod> result = underTest.generateRepaymentPeriods(expectedDisbursementDate, loanApplicationTerms,
|
||||
@@ -166,7 +166,7 @@ public class DefaultScheduledDateGeneratorTest {
|
||||
null, null, null, null, null, Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST,
|
||||
BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO,
|
||||
null, NONE, null, ZERO, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, false,
|
||||
DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false);
|
||||
DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null);
|
||||
}
|
||||
|
||||
private HolidayDetailDTO createHolidayDTO() {
|
||||
|
||||
Reference in New Issue
Block a user