FINERACT-1761-Repayment-due-event-configuration-at-product-level
This commit is contained in:
committed by
Arnold Gálovics
parent
5fe708a90f
commit
57a1cb2f8c
+5
@@ -42,6 +42,11 @@ public class CheckLoanRepaymentDueBusinessStep implements LoanCOBBusinessStep {
|
||||
public Loan execute(Loan loan) {
|
||||
log.debug("start processing loan repayment due business step loan for loan with id [{}]", loan.getId());
|
||||
Long numberOfDaysBeforeDueDateToRaiseEvent = configurationDomainService.retrieveRepaymentDueDays();
|
||||
if (loan.getLoanProduct().getDueDaysForRepaymentEvent() != null) {
|
||||
if (loan.getLoanProduct().getDueDaysForRepaymentEvent() > 0) {
|
||||
numberOfDaysBeforeDueDateToRaiseEvent = loan.getLoanProduct().getDueDaysForRepaymentEvent().longValue();
|
||||
}
|
||||
}
|
||||
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||
final List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
for (LoanRepaymentScheduleInstallment repaymentSchedule : loanRepaymentScheduleInstallments) {
|
||||
|
||||
+5
@@ -42,6 +42,11 @@ public class CheckLoanRepaymentOverdueBusinessStep implements LoanCOBBusinessSte
|
||||
public Loan execute(Loan loan) {
|
||||
log.debug("start processing loan repayment overdue business step for loan with Id [{}]", loan.getId());
|
||||
Long numberOfDaysAfterDueDateToRaiseEvent = configurationDomainService.retrieveRepaymentOverdueDays();
|
||||
if (loan.getLoanProduct().getOverDueDaysForRepaymentEvent() != null) {
|
||||
if (loan.getLoanProduct().getOverDueDaysForRepaymentEvent() > 0) {
|
||||
numberOfDaysAfterDueDateToRaiseEvent = loan.getLoanProduct().getOverDueDaysForRepaymentEvent().longValue();
|
||||
}
|
||||
}
|
||||
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||
final List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
for (LoanRepaymentScheduleInstallment repaymentSchedule : loanRepaymentScheduleInstallments) {
|
||||
|
||||
+4
@@ -143,4 +143,8 @@ public interface LoanProductConstants {
|
||||
String OVER_APPLIED_NUMBER = "overAppliedNumber";
|
||||
String DELINQUENCY_BUCKET_PARAM_NAME = "delinquencyBucketId";
|
||||
|
||||
// repayment events related
|
||||
String DUE_DAYS_FOR_REPAYMENT_EVENT = "dueDaysForRepaymentEvent";
|
||||
String OVER_DUE_DAYS_FOR_REPAYMENT_EVENT = "overDueDaysForRepaymentEvent";
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -106,7 +106,8 @@ public class LoanProductsApiResource {
|
||||
"isLinkedToFloatingInterestRates", "floatingRatesId", "interestRateDifferential", "minDifferentialLendingRate",
|
||||
"defaultDifferentialLendingRate", "maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed",
|
||||
LoanProductConstants.CAN_USE_FOR_TOPUP, LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, LoanProductConstants.RATES_PARAM_NAME,
|
||||
LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName));
|
||||
LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT,
|
||||
LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT));
|
||||
|
||||
private static final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList("productId", "productName", "restrictedProducts", "allowedProducts", "productOptions"));
|
||||
@@ -141,7 +142,7 @@ public class LoanProductsApiResource {
|
||||
@Operation(summary = "Create a Loan Product", description = "Depending of the Accounting Rule (accountingRule) selected, additional fields with details of the appropriate Ledger Account identifiers would need to be passed in.\n"
|
||||
+ "\n" + "Refer MifosX Accounting Specs Draft for more details regarding the significance of the selected accounting rule\n\n"
|
||||
+ "Mandatory Fields: name, shortName, currencyCode, digitsAfterDecimal, inMultiplesOf, principal, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyCode, accountingRule, isInterestRecalculationEnabled, daysInYearType, daysInMonthType\n\n"
|
||||
+ "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, includeInBorrowerCycle, useBorrowerCycle,principalVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides, allowPartialPeriodInterestCalcualtion\n\n"
|
||||
+ "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, includeInBorrowerCycle, useBorrowerCycle,principalVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides, allowPartialPeriodInterestCalcualtion,dueDaysForRepaymentEvent,overDueDaysForRepaymentEvent\n\n"
|
||||
+ "Additional Mandatory Fields for Cash(2) based accounting: fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n"
|
||||
+ "Additional Mandatory Fields for periodic (3) and upfront (4)accrual accounting: fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId, transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n"
|
||||
+ "Additional Mandatory Fields if interest recalculation is enabled(true): interestRecalculationCompoundingMethod, rescheduleStrategyMethod, recalculationRestFrequencyType\n\n"
|
||||
|
||||
+12
@@ -149,6 +149,10 @@ final class LoanProductsApiResourceSwagger {
|
||||
public Boolean holdGuaranteeFunds;
|
||||
@Schema(example = "1")
|
||||
public Long delinquencyBucketId;
|
||||
@Schema(example = "3")
|
||||
public Integer dueDaysForRepaymentEvent;
|
||||
@Schema(example = "3")
|
||||
public Integer overDueDaysForRepaymentEvent;
|
||||
|
||||
// Interest Recalculation
|
||||
@Schema(example = "false")
|
||||
@@ -1191,6 +1195,10 @@ final class LoanProductsApiResourceSwagger {
|
||||
public GetDelinquencyBucketsResponse delinquencyBucket;
|
||||
@Schema(example = "true")
|
||||
public Boolean disallowExpectedDisbursements;
|
||||
@Schema(example = "3")
|
||||
public Integer dueDaysForRepaymentEvent;
|
||||
@Schema(example = "3")
|
||||
public Integer overDueDaysForRepaymentEvent;
|
||||
}
|
||||
|
||||
@Schema(description = "PutLoanProductsProductIdRequest")
|
||||
@@ -1308,6 +1316,10 @@ final class LoanProductsApiResourceSwagger {
|
||||
public Boolean holdGuaranteeFunds;
|
||||
@Schema(example = "1")
|
||||
public Long delinquencyBucketId;
|
||||
@Schema(example = "3")
|
||||
public Integer dueDaysForRepaymentEvent;
|
||||
@Schema(example = "3")
|
||||
public Integer overDueDaysForRepaymentEvent;
|
||||
|
||||
// Interest Recalculation
|
||||
@Schema(example = "false")
|
||||
|
||||
+25
-5
@@ -190,6 +190,9 @@ public class LoanProductData implements Serializable {
|
||||
private final Collection<DelinquencyBucketData> delinquencyBucketOptions;
|
||||
private final DelinquencyBucketData delinquencyBucket;
|
||||
|
||||
private final Integer dueDaysForRepaymentEvent;
|
||||
private final Integer overDueDaysForRepaymentEvent;
|
||||
|
||||
/**
|
||||
* Used when returning lookup information about loan product for dropdowns.
|
||||
*/
|
||||
@@ -276,6 +279,8 @@ public class LoanProductData implements Serializable {
|
||||
final boolean isRatesEnabled = false;
|
||||
final Collection<DelinquencyBucketData> delinquencyBucketOptions = null;
|
||||
final DelinquencyBucketData delinquencyBucket = null;
|
||||
final Integer dueDaysForRepaymentEvent = null;
|
||||
final Integer overDueDaysForRepaymentEvent = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -293,7 +298,8 @@ public class LoanProductData implements Serializable {
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket);
|
||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
||||
overDueDaysForRepaymentEvent);
|
||||
|
||||
}
|
||||
|
||||
@@ -381,6 +387,8 @@ public class LoanProductData implements Serializable {
|
||||
final boolean isRatesEnabled = false;
|
||||
final Collection<DelinquencyBucketData> delinquencyBucketOptions = null;
|
||||
final DelinquencyBucketData delinquencyBucket = null;
|
||||
final Integer dueDaysForRepaymentEvent = null;
|
||||
final Integer overDueDaysForRepaymentEvent = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -398,7 +406,8 @@ public class LoanProductData implements Serializable {
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket);
|
||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
||||
overDueDaysForRepaymentEvent);
|
||||
|
||||
}
|
||||
|
||||
@@ -493,6 +502,8 @@ public class LoanProductData implements Serializable {
|
||||
final boolean isRatesEnabled = false;
|
||||
final Collection<DelinquencyBucketData> delinquencyBucketOptions = null;
|
||||
final DelinquencyBucketData delinquencyBucket = null;
|
||||
final Integer dueDaysForRepaymentEvent = null;
|
||||
final Integer overDueDaysForRepaymentEvent = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -510,7 +521,8 @@ public class LoanProductData implements Serializable {
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket);
|
||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
||||
overDueDaysForRepaymentEvent);
|
||||
|
||||
}
|
||||
|
||||
@@ -599,6 +611,8 @@ public class LoanProductData implements Serializable {
|
||||
final boolean isRatesEnabled = false;
|
||||
final Collection<DelinquencyBucketData> delinquencyBucketOptions = null;
|
||||
final DelinquencyBucketData delinquencyBucket = null;
|
||||
final Integer dueDaysForRepaymentEvent = null;
|
||||
final Integer overDueDaysForRepaymentEvent = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -616,7 +630,8 @@ public class LoanProductData implements Serializable {
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket);
|
||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
||||
overDueDaysForRepaymentEvent);
|
||||
|
||||
}
|
||||
|
||||
@@ -662,7 +677,8 @@ public class LoanProductData implements Serializable {
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization,
|
||||
Collection<RateData> rateOptions, Collection<RateData> rates, final boolean isRatesEnabled,
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment, final Collection<DelinquencyBucketData> delinquencyBucketOptions,
|
||||
final DelinquencyBucketData delinquencyBucket) {
|
||||
final DelinquencyBucketData delinquencyBucket, final Integer dueDaysForRepaymentEvent,
|
||||
final Integer overDueDaysForRepaymentEvent) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -778,6 +794,8 @@ public class LoanProductData implements Serializable {
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
this.delinquencyBucketOptions = delinquencyBucketOptions;
|
||||
this.delinquencyBucket = delinquencyBucket;
|
||||
this.dueDaysForRepaymentEvent = dueDaysForRepaymentEvent;
|
||||
this.overDueDaysForRepaymentEvent = overDueDaysForRepaymentEvent;
|
||||
}
|
||||
|
||||
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
||||
@@ -927,6 +945,8 @@ public class LoanProductData implements Serializable {
|
||||
this.isRatesEnabled = isRatesEnabled;
|
||||
this.delinquencyBucketOptions = delinquencyBucketOptions;
|
||||
this.delinquencyBucket = productData.delinquencyBucket;
|
||||
this.dueDaysForRepaymentEvent = productData.dueDaysForRepaymentEvent;
|
||||
this.overDueDaysForRepaymentEvent = productData.overDueDaysForRepaymentEvent;
|
||||
}
|
||||
|
||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||
|
||||
+40
-2
@@ -202,6 +202,12 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
@JoinColumn(name = "delinquency_bucket_id")
|
||||
private DelinquencyBucket delinquencyBucket;
|
||||
|
||||
@Column(name = "due_days_for_repayment_event")
|
||||
private Integer dueDaysForRepaymentEvent;
|
||||
|
||||
@Column(name = "overdue_days_for_repayment_event")
|
||||
private Integer overDueDaysForRepaymentEvent;
|
||||
|
||||
public static LoanProduct assembleFromJson(final Fund fund, final String loanTransactionProcessingStrategy,
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate,
|
||||
final List<Rate> productRates) {
|
||||
@@ -373,6 +379,10 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
|
||||
final Integer overAppliedNumber = command.integerValueOfParameterNamed(LoanProductConstants.OVER_APPLIED_NUMBER);
|
||||
|
||||
final Integer dueDaysForRepaymentEvent = command.integerValueOfParameterNamed(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT);
|
||||
final Integer overDueDaysForRepaymentEvent = command
|
||||
.integerValueOfParameterNamed(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT);
|
||||
|
||||
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal,
|
||||
maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType,
|
||||
annualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, repaymentEvery,
|
||||
@@ -388,7 +398,8 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
defaultDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed,
|
||||
minimumGapBetweenInstallments, maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup,
|
||||
isEqualAmortization, productRates, fixedPrincipalPercentagePerInstallment, disallowExpectedDisbursements,
|
||||
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber);
|
||||
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent,
|
||||
overDueDaysForRepaymentEvent);
|
||||
|
||||
}
|
||||
|
||||
@@ -599,7 +610,7 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization,
|
||||
final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment, final boolean disallowExpectedDisbursements,
|
||||
final boolean allowApprovedDisbursedAmountsOverApplied, final String overAppliedCalculationType,
|
||||
final Integer overAppliedNumber) {
|
||||
final Integer overAppliedNumber, final Integer dueDaysForRepaymentEvent, final Integer overDueDaysForRepaymentEvent) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
|
||||
this.name = name.trim();
|
||||
@@ -681,6 +692,10 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
if (rates != null) {
|
||||
this.rates = rates;
|
||||
}
|
||||
|
||||
this.dueDaysForRepaymentEvent = dueDaysForRepaymentEvent;
|
||||
this.overDueDaysForRepaymentEvent = overDueDaysForRepaymentEvent;
|
||||
|
||||
validateLoanProductPreSave();
|
||||
}
|
||||
|
||||
@@ -1184,6 +1199,21 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
this.overAppliedNumber = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, this.dueDaysForRepaymentEvent)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT);
|
||||
actualChanges.put(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.dueDaysForRepaymentEvent = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT,
|
||||
this.overDueDaysForRepaymentEvent)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT);
|
||||
actualChanges.put(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.overDueDaysForRepaymentEvent = newValue;
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@@ -1571,4 +1601,12 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
this.delinquencyBucket = delinquencyBucket;
|
||||
}
|
||||
|
||||
public Integer getDueDaysForRepaymentEvent() {
|
||||
return this.dueDaysForRepaymentEvent;
|
||||
}
|
||||
|
||||
public Integer getOverDueDaysForRepaymentEvent() {
|
||||
return this.overDueDaysForRepaymentEvent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-1
@@ -158,7 +158,8 @@ public final class LoanProductDataValidator {
|
||||
LoanProductConstants.CAN_USE_FOR_TOPUP, LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, LoanProductConstants.RATES_PARAM_NAME,
|
||||
LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName, LoanProductConstants.DISALLOW_EXPECTED_DISBURSEMENTS,
|
||||
LoanProductConstants.ALLOW_APPROVED_DISBURSED_AMOUNTS_OVER_APPLIED, LoanProductConstants.OVER_APPLIED_CALCULATION_TYPE,
|
||||
LoanProductConstants.OVER_APPLIED_NUMBER, LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME));
|
||||
LoanProductConstants.OVER_APPLIED_NUMBER, LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME,
|
||||
LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT));
|
||||
|
||||
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
|
||||
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
|
||||
@@ -733,6 +734,16 @@ public final class LoanProductDataValidator {
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.CAN_USE_FOR_TOPUP).value(canUseForTopup).validateForBooleanValue();
|
||||
}
|
||||
|
||||
final Integer dueDaysForRepaymentEvent = this.fromApiJsonHelper
|
||||
.extractIntegerWithLocaleNamed(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT).value(dueDaysForRepaymentEvent)
|
||||
.integerZeroOrGreater();
|
||||
|
||||
final Integer overDueDaysForRepaymentEvent = this.fromApiJsonHelper
|
||||
.extractIntegerWithLocaleNamed(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT).value(overDueDaysForRepaymentEvent)
|
||||
.integerZeroOrGreater();
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -1600,6 +1611,16 @@ public final class LoanProductDataValidator {
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.CAN_USE_FOR_TOPUP).value(canUseForTopup).validateForBooleanValue();
|
||||
}
|
||||
|
||||
final Integer dueDaysForRepaymentEvent = this.fromApiJsonHelper
|
||||
.extractIntegerWithLocaleNamed(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT).value(dueDaysForRepaymentEvent)
|
||||
.integerZeroOrGreater();
|
||||
|
||||
final Integer overDueDaysForRepaymentEvent = this.fromApiJsonHelper
|
||||
.extractIntegerWithLocaleNamed(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT).value(overDueDaysForRepaymentEvent)
|
||||
.integerZeroOrGreater();
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -216,6 +216,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "lp.disallow_expected_disbursements as disallowExpectedDisbursements, lp.allow_approved_disbursed_amounts_over_applied as allowApprovedDisbursedAmountsOverApplied, lp.over_applied_calculation_type as overAppliedCalculationType, over_applied_number as overAppliedNumber, "
|
||||
+ "lp.days_in_month_enum as daysInMonth, lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as isInterestRecalculationEnabled, "
|
||||
+ "lp.can_define_fixed_emi_amount as canDefineInstallmentAmount, lp.instalment_amount_in_multiples_of as installmentAmountInMultiplesOf, "
|
||||
+ "lp.due_days_for_repayment_event as dueDaysForRepaymentEvent, lp.overdue_days_for_repayment_event as overDueDaysForRepaymentEvent,"
|
||||
+ "lpr.pre_close_interest_calculation_strategy as preCloseInterestCalculationStrategy, "
|
||||
+ "lpr.id as lprId, lpr.product_id as productId, lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as rescheduleStrategy, "
|
||||
+ "lpr.rest_frequency_type_enum as restFrequencyEnum, lpr.rest_frequency_interval as restFrequencyInterval, "
|
||||
@@ -345,6 +346,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final boolean useBorrowerCycle = rs.getBoolean("useBorrowerCycle");
|
||||
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate");
|
||||
final LocalDate closeDate = JdbcSupport.getLocalDate(rs, "closeDate");
|
||||
final Integer dueDaysForRepaymentEvent = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "dueDaysForRepaymentEvent");
|
||||
final Integer overDueDaysForRepaymentEvent = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "overDueDaysForRepaymentEvent");
|
||||
String status = "";
|
||||
if (closeDate != null && closeDate.isBefore(DateUtils.getBusinessLocalDate())) {
|
||||
status = "loanProduct.inActive";
|
||||
@@ -499,7 +502,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
|
||||
maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, this.rates,
|
||||
isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket);
|
||||
isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket,
|
||||
dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,4 +123,5 @@
|
||||
<include file="parts/0101_update_transaction_summary_table_report.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0102_add_external_event_for_loan_reschedule.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0103_modify_parameter_json_column_custom_job_parameters.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0104_loan_product_add_repayment_overdue_days_config.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addColumn tableName="m_product_loan">
|
||||
<column name="due_days_for_repayment_event" type="INT">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_product_loan">
|
||||
<column name="overdue_days_for_repayment_event" type="INT">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+35
@@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.repayment.L
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -75,10 +76,13 @@ public class CheckLoanRepaymentDueBusinessStepTest {
|
||||
when(configurationDomainService.retrieveRepaymentDueDays()).thenReturn(1L);
|
||||
LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().plusDays(1);
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
LoanRepaymentScheduleInstallment repaymentInstallment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1,
|
||||
LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
||||
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0));
|
||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallment);
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(null);
|
||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||
|
||||
// when
|
||||
@@ -97,10 +101,13 @@ public class CheckLoanRepaymentDueBusinessStepTest {
|
||||
when(configurationDomainService.retrieveRepaymentDueDays()).thenReturn(1L);
|
||||
LocalDate loanInstallmentRepaymentDueDateAfter5Days = DateUtils.getBusinessLocalDate().plusDays(5);
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays
|
||||
.asList(new LoanRepaymentScheduleInstallment(loanForProcessing, 1, LocalDate.now(ZoneId.systemDefault()),
|
||||
loanInstallmentRepaymentDueDateAfter5Days, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
||||
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(null);
|
||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||
|
||||
// when
|
||||
@@ -110,4 +117,32 @@ public class CheckLoanRepaymentDueBusinessStepTest {
|
||||
assertEquals(processedLoan, loanForProcessing);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoanWithInstallmentDueAfterConfiguredDaysInLoanProductWhenStepExecutionThenBusinessEventIsRaised() {
|
||||
ArgumentCaptor<LoanRepaymentDueBusinessEvent> loanRepaymentDueEvent = ArgumentCaptor.forClass(LoanRepaymentDueBusinessEvent.class);
|
||||
// given
|
||||
// Global config settings
|
||||
when(configurationDomainService.retrieveRepaymentDueDays()).thenReturn(2L);
|
||||
LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().plusDays(1);
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
LoanRepaymentScheduleInstallment repaymentInstallment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1,
|
||||
LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
||||
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0));
|
||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallment);
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
// Loan Product setting overrides global settings
|
||||
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(1);
|
||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||
|
||||
// when
|
||||
Loan processedLoan = underTest.execute(loanForProcessing);
|
||||
// then
|
||||
verify(businessEventNotifierService, times(1)).notifyPostBusinessEvent(loanRepaymentDueEvent.capture());
|
||||
LoanRepaymentScheduleInstallment loanPayloadForEvent = loanRepaymentDueEvent.getValue().get();
|
||||
assertEquals(repaymentInstallment, loanPayloadForEvent);
|
||||
assertEquals(processedLoan, loanForProcessing);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.repayment.L
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -76,10 +77,13 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
||||
when(configurationDomainService.retrieveRepaymentOverdueDays()).thenReturn(1L);
|
||||
LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().minusDays(1);
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
LoanRepaymentScheduleInstallment repaymentInstallment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1,
|
||||
LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
||||
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0));
|
||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallment);
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||
|
||||
// when
|
||||
@@ -97,10 +101,13 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
||||
when(configurationDomainService.retrieveRepaymentOverdueDays()).thenReturn(1L);
|
||||
LocalDate loanInstallmentRepaymentDueDateBefore5Days = DateUtils.getBusinessLocalDate().minusDays(5);
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays
|
||||
.asList(new LoanRepaymentScheduleInstallment(loanForProcessing, 1, LocalDate.now(ZoneId.systemDefault()),
|
||||
loanInstallmentRepaymentDueDateBefore5Days, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
||||
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||
// when
|
||||
Loan processedLoan = underTest.execute(loanForProcessing);
|
||||
@@ -116,6 +123,7 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
||||
when(configurationDomainService.retrieveRepaymentOverdueDays()).thenReturn(1L);
|
||||
LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().minusDays(1);
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
LoanRepaymentScheduleInstallment repaymentInstallmentPaidOff = new LoanRepaymentScheduleInstallment(loanForProcessing, 1,
|
||||
LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
||||
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0));
|
||||
@@ -123,6 +131,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
||||
repaymentInstallmentPaidOff.updateObligationMet(true);
|
||||
|
||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallmentPaidOff);
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
|
||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||
|
||||
// when
|
||||
@@ -132,4 +142,32 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
||||
assertEquals(processedLoan, loanForProcessing);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoanWithInstallmentOverdueAfterConfiguredDaysInLoanProductWhenStepExecutionThenBusinessEventIsRaised() {
|
||||
ArgumentCaptor<LoanRepaymentOverdueBusinessEvent> loanRepaymentDueBusinessEventArgumentCaptor = ArgumentCaptor
|
||||
.forClass(LoanRepaymentOverdueBusinessEvent.class);
|
||||
// given
|
||||
// global configuration
|
||||
when(configurationDomainService.retrieveRepaymentOverdueDays()).thenReturn(2L);
|
||||
LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().minusDays(1);
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
LoanRepaymentScheduleInstallment repaymentInstallment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1,
|
||||
LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
|
||||
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0));
|
||||
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallment);
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
// product configuration overrides global configuration
|
||||
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(1);
|
||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
|
||||
|
||||
// when
|
||||
Loan processedLoan = underTest.execute(loanForProcessing);
|
||||
// then
|
||||
verify(businessEventNotifierService, times(1)).notifyPostBusinessEvent(loanRepaymentDueBusinessEventArgumentCaptor.capture());
|
||||
LoanRepaymentScheduleInstallment loanPayloadForEvent = loanRepaymentDueBusinessEventArgumentCaptor.getValue().get();
|
||||
assertEquals(repaymentInstallment, loanPayloadForEvent);
|
||||
assertEquals(processedLoan, loanForProcessing);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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.integrationtests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import io.restassured.builder.RequestSpecBuilder;
|
||||
import io.restassured.builder.ResponseSpecBuilder;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
|
||||
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||
import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
|
||||
import org.apache.fineract.client.models.PutLoanProductsProductIdResponse;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class LoanProductWithRepaymentDueEventConfigurationTest {
|
||||
|
||||
private ResponseSpecification responseSpec;
|
||||
private RequestSpecification requestSpec;
|
||||
private ClientHelper clientHelper;
|
||||
private LoanTransactionHelper loanTransactionHelper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
Utils.initializeRESTAssured();
|
||||
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
this.clientHelper = new ClientHelper(this.requestSpec, this.responseSpec);
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loanProductCreationWithDueDaysConfigurationForRepaymentEventTest() {
|
||||
// Loan ExternalId
|
||||
String loanExternalIdStr = UUID.randomUUID().toString();
|
||||
|
||||
// Delinquency Bucket
|
||||
final Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
|
||||
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
|
||||
delinquencyBucketId);
|
||||
|
||||
// event days configuration
|
||||
Integer dueDaysForRepaymentEvent = 1;
|
||||
Integer overDueDaysForRepaymentEvent = 2;
|
||||
|
||||
// Client and Loan account creation
|
||||
|
||||
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
|
||||
Integer loanProductId = createLoanProductWithDueDaysForRepaymentEvent(loanTransactionHelper, delinquencyBucketId,
|
||||
dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent);
|
||||
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = loanTransactionHelper.getLoanProduct(loanProductId);
|
||||
assertNotNull(getLoanProductsProductResponse);
|
||||
assertNotNull(getLoanProductsProductResponse.getDueDaysForRepaymentEvent());
|
||||
assertNotNull(getLoanProductsProductResponse.getOverDueDaysForRepaymentEvent());
|
||||
assertEquals(getLoanProductsProductResponse.getDueDaysForRepaymentEvent(), dueDaysForRepaymentEvent);
|
||||
assertEquals(getLoanProductsProductResponse.getOverDueDaysForRepaymentEvent(), overDueDaysForRepaymentEvent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loanProductUpdateWithDueDaysConfigurationForRepaymentEventTest() {
|
||||
// Loan ExternalId
|
||||
String loanExternalIdStr = UUID.randomUUID().toString();
|
||||
|
||||
// Delinquency Bucket
|
||||
final Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
|
||||
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
|
||||
delinquencyBucketId);
|
||||
|
||||
// Client and Loan account creation
|
||||
|
||||
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
|
||||
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
|
||||
delinquencyBucketId);
|
||||
assertNotNull(getLoanProductsProductResponse);
|
||||
|
||||
// Modify Loan Product
|
||||
PutLoanProductsProductIdResponse loanProductModifyResponse = updateLoanProduct(loanTransactionHelper,
|
||||
getLoanProductsProductResponse.getId());
|
||||
assertNotNull(loanProductModifyResponse);
|
||||
|
||||
}
|
||||
|
||||
private PutLoanProductsProductIdResponse updateLoanProduct(LoanTransactionHelper loanTransactionHelper, Long id) {
|
||||
// event days configuration
|
||||
Integer dueDaysForRepaymentEvent = 1;
|
||||
Integer overDueDaysForRepaymentEvent = 2;
|
||||
final PutLoanProductsProductIdRequest requestModifyLoan = new PutLoanProductsProductIdRequest()
|
||||
.dueDaysForRepaymentEvent(dueDaysForRepaymentEvent).overDueDaysForRepaymentEvent(overDueDaysForRepaymentEvent).locale("en");
|
||||
return loanTransactionHelper.updateLoanProduct(id, requestModifyLoan);
|
||||
}
|
||||
|
||||
private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
|
||||
final Integer delinquencyBucketId) {
|
||||
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId);
|
||||
final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
|
||||
return loanTransactionHelper.getLoanProduct(loanProductId);
|
||||
}
|
||||
|
||||
private Integer createLoanProductWithDueDaysForRepaymentEvent(final LoanTransactionHelper loanTransactionHelper,
|
||||
final Integer delinquencyBucketId, Integer dueDaysForRepaymentEvent, Integer overDueDaysForRepaymentEvent) {
|
||||
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().withDueDaysForRepaymentEvent(dueDaysForRepaymentEvent)
|
||||
.withOverDueDaysForRepaymentEvent(overDueDaysForRepaymentEvent).build(null, delinquencyBucketId);
|
||||
final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
|
||||
return loanProductId;
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -138,6 +138,8 @@ public class LoanProductTestBuilder {
|
||||
private String installmentAmountInMultiplesOf;
|
||||
private boolean canDefineInstallmentAmount;
|
||||
private Integer delinquencyBucketId;
|
||||
private Integer dueDaysForRepaymentEvent = null;
|
||||
private Integer overDueDaysForRepaymentEvent = null;
|
||||
|
||||
public String build(final String chargeId) {
|
||||
final HashMap<String, Object> map = build(chargeId, null);
|
||||
@@ -267,6 +269,13 @@ public class LoanProductTestBuilder {
|
||||
map.put("penaltyToIncomeAccountMappings", this.penaltyToIncomeAccountMappings);
|
||||
}
|
||||
|
||||
if (this.dueDaysForRepaymentEvent != null) {
|
||||
map.put("dueDaysForRepaymentEvent", this.dueDaysForRepaymentEvent);
|
||||
}
|
||||
if (this.overDueDaysForRepaymentEvent != null) {
|
||||
map.put("overDueDaysForRepaymentEvent", this.overDueDaysForRepaymentEvent);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -656,4 +665,15 @@ public class LoanProductTestBuilder {
|
||||
this.feeAndPenaltyAssetAccount = account;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withDueDaysForRepaymentEvent(final Integer dueDaysForRepaymentEvent) {
|
||||
this.dueDaysForRepaymentEvent = dueDaysForRepaymentEvent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withOverDueDaysForRepaymentEvent(final Integer overDueDaysForRepaymentEvent) {
|
||||
this.overDueDaysForRepaymentEvent = overDueDaysForRepaymentEvent;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -70,6 +70,8 @@ import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||
import org.apache.fineract.client.models.PutChargeTransactionChangesRequest;
|
||||
import org.apache.fineract.client.models.PutChargeTransactionChangesResponse;
|
||||
import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
|
||||
import org.apache.fineract.client.models.PutLoanProductsProductIdResponse;
|
||||
import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdRequest;
|
||||
import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdResponse;
|
||||
import org.apache.fineract.client.models.PutLoansLoanIdRequest;
|
||||
@@ -1844,4 +1846,8 @@ public class LoanTransactionHelper extends IntegrationTest {
|
||||
final String get = Utils.performServerGet(requestSpec, responseSpec, GET_LOAN_URL, null);
|
||||
return new Gson().fromJson(get, new TypeToken<ArrayList<Integer>>() {}.getType());
|
||||
}
|
||||
|
||||
public PutLoanProductsProductIdResponse updateLoanProduct(Long id, PutLoanProductsProductIdRequest requestModifyLoan) {
|
||||
return ok(fineract().loanProducts.updateLoanProduct(id, requestModifyLoan));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user