Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,6 @@ public interface LoanProductConstants {
String ADVANCED_PAYMENT_ALLOCATION_STRATEGY = "advanced-payment-allocation-strategy";

String FIXED_LENGTH = "fixedLength";

String USE_DUE_REPAYMENT_GLOBAL_CONFIGS = "useDueRepaymentGlobalConfigs";
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ private PostLoanProductsRequest() {}
public Long delinquencyBucketId;
@Schema(example = "false")
public Boolean enableInstallmentLevelDelinquency;
@Schema(example = "false")
public Boolean useDueRepaymentGlobalConfigs;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer the following:

  • We dont have a new field that contains a boolean value to decide whether something got fetched from global configuration or not.

Instead, if we have any settings on the loan product, fetch it, if not, we read from global configuration.

User can decide whether they provide any value for it or not... Basically these field would be optional ones, and what we can do when we fetch the loan product or the loan product template, we can send back the values from the global configuration and as a hint it can be shown on the UI. After that the user can decide whether they are happy with it or wanna override it. What do you think?
@bharathc27

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamsaghy / @bharathc27

This boolean attribute is to let the UI know If the products uses or not the global config values. This is because in the UI exists a checkbox to indicate this. When you are creating and select that checkbox the global values are sent to Fineract. Until this, It is ok

The problem exists when you edit a Loan Product, the UI can not to know If the values of these configurations are or not copied from the global config

This issue was reported by Peter. The UI has the logic to read the value, but If Fineract does not return any value for this, then the UI can not indicate if the configurations are or not inherit from the global configurations

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still believe we should not have the which indicates whether it is coming from global configuration or not. I would simply leave these fields as optional and if user does not fill them, it is coming from global configurations. If user provide a value then that will be used instead of the one from global configurations. Additionally we can enhance further and return the values from global configurations and we can show it on the UI to inform the user if nothing is sent, those value will be used, but they should be different values and do not send it back to backend!

What are your thoughts?

@Schema(example = "3")
public Integer dueDaysForRepaymentEvent;
@Schema(example = "3")
Expand Down Expand Up @@ -1260,6 +1262,8 @@ private GetLoanCharge() {}
public Boolean enableInstallmentLevelDelinquency;
@Schema(example = "true")
public Boolean disallowExpectedDisbursements;
@Schema(example = "false")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this for GetLoanCharge?

public Boolean useDueRepaymentGlobalConfigs;
@Schema(example = "3")
public Integer dueDaysForRepaymentEvent;
@Schema(example = "3")
Expand Down Expand Up @@ -1402,6 +1406,8 @@ private PutLoanProductsProductIdRequest() {}
public Long delinquencyBucketId;
@Schema(example = "false")
public Boolean enableInstallmentLevelDelinquency;
@Schema(example = "false")
public Boolean useDueRepaymentGlobalConfigs;
@Schema(example = "3")
public Integer dueDaysForRepaymentEvent;
@Schema(example = "3")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ public class LoanProductData implements Serializable {
private final Collection<DelinquencyBucketData> delinquencyBucketOptions;
private final DelinquencyBucketData delinquencyBucket;

private final boolean useDueRepaymentGlobalConfigs;
private final Integer dueDaysForRepaymentEvent;
private final Integer overDueDaysForRepaymentEvent;

Expand Down Expand Up @@ -310,6 +311,7 @@ public static LoanProductData lookup(final Long id, final String name, final Boo
final boolean isRatesEnabled = false;
final Collection<DelinquencyBucketData> delinquencyBucketOptions = null;
final DelinquencyBucketData delinquencyBucket = null;
final boolean useDueRepaymentGlobalConfigs = false;
final Integer dueDaysForRepaymentEvent = null;
final Integer overDueDaysForRepaymentEvent = null;
final boolean enableDownPayment = false;
Expand Down Expand Up @@ -343,7 +345,7 @@ public static LoanProductData lookup(final Long id, final String name, final Boo
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength);
loanScheduleProcessingType, fixedLength, useDueRepaymentGlobalConfigs);

}

Expand Down Expand Up @@ -432,6 +434,7 @@ public static LoanProductData lookupWithCurrency(final Long id, final String nam
final boolean isRatesEnabled = false;
final Collection<DelinquencyBucketData> delinquencyBucketOptions = null;
final DelinquencyBucketData delinquencyBucket = null;
final boolean useDueRepaymentGlobalConfigs = false;
final Integer dueDaysForRepaymentEvent = null;
final Integer overDueDaysForRepaymentEvent = null;
final boolean enableDownPayment = false;
Expand Down Expand Up @@ -463,7 +466,7 @@ public static LoanProductData lookupWithCurrency(final Long id, final String nam
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength);
loanScheduleProcessingType, fixedLength, useDueRepaymentGlobalConfigs);

}

Expand Down Expand Up @@ -559,6 +562,7 @@ public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
final boolean isRatesEnabled = false;
final Collection<DelinquencyBucketData> delinquencyBucketOptions = null;
final DelinquencyBucketData delinquencyBucket = null;
final boolean useDueRepaymentGlobalConfigs = false;
final Integer dueDaysForRepaymentEvent = null;
final Integer overDueDaysForRepaymentEvent = null;
final boolean enableDownPayment = false;
Expand Down Expand Up @@ -590,7 +594,7 @@ public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength);
loanScheduleProcessingType, fixedLength, useDueRepaymentGlobalConfigs);

}

Expand Down Expand Up @@ -680,6 +684,7 @@ public static LoanProductData loanProductWithFloatingRates(final Long id, final
final boolean isRatesEnabled = false;
final Collection<DelinquencyBucketData> delinquencyBucketOptions = null;
final DelinquencyBucketData delinquencyBucket = null;
final boolean useDueRepaymentGlobalConfigs = false;
final Integer dueDaysForRepaymentEvent = null;
final Integer overDueDaysForRepaymentEvent = null;
final boolean enableDownPayment = false;
Expand Down Expand Up @@ -711,7 +716,7 @@ public static LoanProductData loanProductWithFloatingRates(final Long id, final
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength);
loanScheduleProcessingType, fixedLength, useDueRepaymentGlobalConfigs);
}

public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
Expand Down Expand Up @@ -761,7 +766,8 @@ public LoanProductData(final Long id, final String name, final String shortName,
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment,
final Collection<AdvancedPaymentData> paymentAllocation, final Collection<CreditAllocationData> creditAllocation,
final EnumOptionData repaymentStartDateType, final boolean enableInstallmentLevelDelinquency,
final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength) {
final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength,
final boolean useDueRepaymentGlobalConfigs) {
this.id = id;
this.name = name;
this.shortName = shortName;
Expand Down Expand Up @@ -878,6 +884,7 @@ public LoanProductData(final Long id, final String name, final String shortName,
this.isEqualAmortization = isEqualAmortization;
this.delinquencyBucketOptions = delinquencyBucketOptions;
this.delinquencyBucket = delinquencyBucket;
this.useDueRepaymentGlobalConfigs = useDueRepaymentGlobalConfigs;
this.dueDaysForRepaymentEvent = dueDaysForRepaymentEvent;
this.overDueDaysForRepaymentEvent = overDueDaysForRepaymentEvent;
this.enableDownPayment = enableDownPayment;
Expand Down Expand Up @@ -1053,6 +1060,7 @@ public LoanProductData(final LoanProductData productData, final Collection<Charg
this.isRatesEnabled = isRatesEnabled;
this.delinquencyBucketOptions = delinquencyBucketOptions;
this.delinquencyBucket = productData.delinquencyBucket;
this.useDueRepaymentGlobalConfigs = productData.useDueRepaymentGlobalConfigs;
this.dueDaysForRepaymentEvent = productData.dueDaysForRepaymentEvent;
this.overDueDaysForRepaymentEvent = productData.overDueDaysForRepaymentEvent;
this.enableDownPayment = productData.enableDownPayment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
@Column(name = "repayment_start_date_type_enum", nullable = false)
private RepaymentStartDateType repaymentStartDateType;

@Column(name = "use_due_repayment_global_configs", nullable = false)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was not stored previously?

private boolean useDueRepaymentGlobalConfigs = false;

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, List<LoanProductPaymentAllocationRule> loanProductPaymentAllocationRules,
Expand Down Expand Up @@ -439,6 +442,9 @@ public static LoanProduct assembleFromJson(final Fund fund, final String loanTra

final Integer fixedLength = command.integerValueOfParameterNamed(LoanProductConstants.FIXED_LENGTH);

final boolean useDueRepaymentGlobalConfigs = command
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.USE_DUE_REPAYMENT_GLOBAL_CONFIGS);

return new LoanProduct(fund, loanTransactionProcessingStrategy, loanProductPaymentAllocationRules, loanProductCreditAllocationRules,
name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod,
Expand All @@ -457,7 +463,8 @@ public static LoanProduct assembleFromJson(final Fund fund, final String loanTra
isEqualAmortization, productRates, fixedPrincipalPercentagePerInstallment, disallowExpectedDisbursements,
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength);
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
useDueRepaymentGlobalConfigs);

}

Expand Down Expand Up @@ -674,7 +681,8 @@ public LoanProduct(final Fund fund, final String transactionProcessingStrategyCo
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
final boolean enableAutoRepaymentForDownPayment, final RepaymentStartDateType repaymentStartDateType,
final boolean enableInstallmentLevelDelinquency, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength) {
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean useDueRepaymentGlobalConfigs) {
this.fund = fund;
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;

Expand Down Expand Up @@ -771,6 +779,7 @@ public LoanProduct(final Fund fund, final String transactionProcessingStrategyCo
this.rates = rates;
}

this.useDueRepaymentGlobalConfigs = useDueRepaymentGlobalConfigs;
this.dueDaysForRepaymentEvent = dueDaysForRepaymentEvent;
this.overDueDaysForRepaymentEvent = overDueDaysForRepaymentEvent;
this.repaymentStartDateType = repaymentStartDateType;
Expand Down Expand Up @@ -1277,6 +1286,13 @@ public Map<String, Object> update(final JsonCommand command, final AprCalculator
this.overAppliedNumber = newValue;
}

if (command.isChangeInBooleanParameterNamed(LoanProductConstants.USE_DUE_REPAYMENT_GLOBAL_CONFIGS,
this.useDueRepaymentGlobalConfigs)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.USE_DUE_REPAYMENT_GLOBAL_CONFIGS);
actualChanges.put(LoanProductConstants.USE_DUE_REPAYMENT_GLOBAL_CONFIGS, newValue);
this.useDueRepaymentGlobalConfigs = 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,22 @@
*/
package org.apache.fineract.portfolio.loanproduct.handler;

import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.loanproduct.service.LoanProductWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@CommandType(entity = "LOANPRODUCT", action = "CREATE")
@RequiredArgsConstructor
public class CreateLoanProductCommandHandler implements NewCommandSourceHandler {

private final LoanProductWritePlatformService writePlatformService;

@Autowired
public CreateLoanProductCommandHandler(final LoanProductWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}

@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,22 @@
*/
package org.apache.fineract.portfolio.loanproduct.handler;

import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.loanproduct.service.LoanProductWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@CommandType(entity = "LOANPRODUCT", action = "UPDATE")
@RequiredArgsConstructor
public class UpdateLoanProductCommandHandler implements NewCommandSourceHandler {

private final LoanProductWritePlatformService writePlatformService;

@Autowired
public UpdateLoanProductCommandHandler(final LoanProductWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}

@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@
<include relativeToChangelogFile="true" file="parts/1019_add_fixed_length.xml"/>
<include relativeToChangelogFile="true" file="parts/1020_add_re_aged_flag_to_loan_installment.xml"/>
<include relativeToChangelogFile="true" file="parts/1021_add_loan_status_change_history.xml"/>
<include relativeToChangelogFile="true" file="parts/1022_add_use_due_repayment_global_configs_to_product.xml"/>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?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.3.xsd">
<changeSet author="fineract" id="1">
<addColumn tableName="m_product_loan">
<column name="use_due_repayment_global_configs" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ public final class LoanProductDataValidator {
LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT,
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.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH,
LoanProductConstants.USE_DUE_REPAYMENT_GLOBAL_CONFIGS));

private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
Expand Down Expand Up @@ -568,7 +569,6 @@ public void validateForCreate(final JsonCommand command) {
}

// Fixed Length validation

fixedLengthValidations(transactionProcessingStrategyCode, isInterestBearing, numberOfRepayments, repaymentEvery, element,
baseDataValidator);

Expand Down Expand Up @@ -757,6 +757,13 @@ public void validateForCreate(final JsonCommand command) {
baseDataValidator.reset().parameter(LoanProductConstants.CAN_USE_FOR_TOPUP).value(canUseForTopup).validateForBooleanValue();
}

if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.USE_DUE_REPAYMENT_GLOBAL_CONFIGS, element)) {
final Boolean useDueRepaymentGlobalConfig = this.fromApiJsonHelper
.extractBooleanNamed(LoanProductConstants.USE_DUE_REPAYMENT_GLOBAL_CONFIGS, element);
baseDataValidator.reset().parameter(LoanProductConstants.USE_DUE_REPAYMENT_GLOBAL_CONFIGS).value(useDueRepaymentGlobalConfig)
.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)
Expand Down
Loading