FINERACT-2398: Re-amortization Interest Handling configuration
This commit is contained in:
committed by
Adam Saghy
parent
8a86759773
commit
a849e9bae3
+1
@@ -120,6 +120,7 @@
|
|||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
||||||
|
<class>org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
||||||
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
||||||
|
|||||||
+3
@@ -142,6 +142,8 @@ public interface LoanApiConstants {
|
|||||||
String CHARGE_OFF_REASONS = "ChargeOffReasons";
|
String CHARGE_OFF_REASONS = "ChargeOffReasons";
|
||||||
// loan ReAge
|
// loan ReAge
|
||||||
String REAGE_REASONS = "ReAgeReasons";
|
String REAGE_REASONS = "ReAgeReasons";
|
||||||
|
// loan ReAmortization
|
||||||
|
String REAMORTIZATION_REASONS = "ReAmortizationReasons";
|
||||||
// fore closure constants
|
// fore closure constants
|
||||||
String transactionDateParamName = "transactionDate";
|
String transactionDateParamName = "transactionDate";
|
||||||
String noteParamName = "note";
|
String noteParamName = "note";
|
||||||
@@ -186,6 +188,7 @@ public interface LoanApiConstants {
|
|||||||
String BUY_DOWN_FEE_COMMAND = "buyDownFee";
|
String BUY_DOWN_FEE_COMMAND = "buyDownFee";
|
||||||
String BUY_DOWN_FEE_ADJUSTMENT_COMMAND = "buyDownFeeAdjustment";
|
String BUY_DOWN_FEE_ADJUSTMENT_COMMAND = "buyDownFeeAdjustment";
|
||||||
String REAGE_COMMAND = "reAge";
|
String REAGE_COMMAND = "reAge";
|
||||||
|
String REAMORTIZATION_COMMAND = "reAmortization";
|
||||||
|
|
||||||
// Data Validator names
|
// Data Validator names
|
||||||
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";
|
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";
|
||||||
|
|||||||
+3
@@ -23,4 +23,7 @@ public interface LoanReAmortizationApiConstants {
|
|||||||
String localeParameterName = "locale";
|
String localeParameterName = "locale";
|
||||||
String dateFormatParameterName = "dateFormat";
|
String dateFormatParameterName = "dateFormat";
|
||||||
String externalIdParameterName = "externalId";
|
String externalIdParameterName = "externalId";
|
||||||
|
|
||||||
|
String reAmortizationInterestHandlingParamName = "reAmortizationInterestHandling";
|
||||||
|
String reasonCodeValueIdParamName = "reasonCodeValueId";
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -370,6 +370,8 @@ final class LoanTransactionsApiResourceSwagger {
|
|||||||
public Integer numberOfInstallments;
|
public Integer numberOfInstallments;
|
||||||
@Schema(example = "DEFAULT")
|
@Schema(example = "DEFAULT")
|
||||||
public String reAgeInterestHandling;
|
public String reAgeInterestHandling;
|
||||||
|
@Schema(example = "DEFAULT")
|
||||||
|
public String reAmortizationInterestHandling;
|
||||||
@Schema(example = "1")
|
@Schema(example = "1")
|
||||||
public Long reasonCodeValueId;
|
public Long reasonCodeValueId;
|
||||||
|
|
||||||
|
|||||||
+2
@@ -121,6 +121,8 @@ public class LoanTransactionData implements Serializable {
|
|||||||
private Collection<CodeValueData> reAgeReasonOptions = null;
|
private Collection<CodeValueData> reAgeReasonOptions = null;
|
||||||
private Collection<PeriodFrequencyType> periodFrequencyOptions = null;
|
private Collection<PeriodFrequencyType> periodFrequencyOptions = null;
|
||||||
private Collection<StringEnumOptionData> reAgeInterestHandlingOptions = null;
|
private Collection<StringEnumOptionData> reAgeInterestHandlingOptions = null;
|
||||||
|
private Collection<CodeValueData> reAmortizationReasonOptions = null;
|
||||||
|
private Collection<StringEnumOptionData> reAmortizationInterestHandlingOptions = null;
|
||||||
|
|
||||||
public static LoanTransactionData importInstance(BigDecimal repaymentAmount, LocalDate lastRepaymentDate, Long repaymentTypeId,
|
public static LoanTransactionData importInstance(BigDecimal repaymentAmount, LocalDate lastRepaymentDate, Long repaymentTypeId,
|
||||||
Integer rowIndex, String locale, String dateFormat) {
|
Integer rowIndex, String locale, String dateFormat) {
|
||||||
|
|||||||
+5
@@ -48,6 +48,7 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
|||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
|
|
||||||
@@ -145,6 +146,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction")
|
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction")
|
||||||
private LoanReAgeParameter loanReAgeParameter;
|
private LoanReAgeParameter loanReAgeParameter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction")
|
||||||
|
private LoanReAmortizationParameter loanReAmortizationParameter;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "classification_cv_id")
|
@JoinColumn(name = "classification_cv_id")
|
||||||
|
|||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* 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.loanaccount.domain.reamortization;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.StringEnumOptionData;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public enum LoanReAmortizationInterestHandlingType {
|
||||||
|
|
||||||
|
DEFAULT("loanReAmortizationInterestHandlingType.default", "Default"), //
|
||||||
|
WAIVE_INTEREST("loanReAmortizationInterestHandlingType.waiveInterest", "Waive Interest"), //
|
||||||
|
EQUAL_AMORTIZATION_INTEREST_SPLIT("loanReAmortizationInterestHandlingType.equalAmortizationInterestSplit",
|
||||||
|
"Equal Amortization Interest Split"), ///
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String humanReadableName;
|
||||||
|
|
||||||
|
public static List<StringEnumOptionData> getValuesAsEnumOptionDataList() {
|
||||||
|
return Arrays.stream(values()).map(v -> new StringEnumOptionData(v.name(), v.getCode(), v.getHumanReadableName())).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringEnumOptionData asEnumOptionData() {
|
||||||
|
return new StringEnumOptionData(name(), getCode(), getHumanReadableName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* 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.loanaccount.domain.reamortization;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_loan_reamortization_parameter")
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class LoanReAmortizationParameter extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@OneToOne
|
||||||
|
@JoinColumn(name = "loan_transaction_id", nullable = false)
|
||||||
|
private LoanTransaction loanTransaction;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "interest_handling_type")
|
||||||
|
private LoanReAmortizationInterestHandlingType interestHandlingType;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "reamortization_reason_code_value_id", nullable = true)
|
||||||
|
private CodeValue reamortizationReason;
|
||||||
|
|
||||||
|
// for JPA, don't use
|
||||||
|
protected LoanReAmortizationParameter() {}
|
||||||
|
}
|
||||||
+2
@@ -34,8 +34,10 @@ public interface LoanTransactionMapper {
|
|||||||
@Mapping(target = "writeOffReasonOptions", ignore = true)
|
@Mapping(target = "writeOffReasonOptions", ignore = true)
|
||||||
@Mapping(target = "chargeOffReasonOptions", ignore = true)
|
@Mapping(target = "chargeOffReasonOptions", ignore = true)
|
||||||
@Mapping(target = "reAgeReasonOptions", ignore = true)
|
@Mapping(target = "reAgeReasonOptions", ignore = true)
|
||||||
|
@Mapping(target = "reAmortizationReasonOptions", ignore = true)
|
||||||
@Mapping(target = "periodFrequencyOptions", ignore = true)
|
@Mapping(target = "periodFrequencyOptions", ignore = true)
|
||||||
@Mapping(target = "reAgeInterestHandlingOptions", ignore = true)
|
@Mapping(target = "reAgeInterestHandlingOptions", ignore = true)
|
||||||
|
@Mapping(target = "reAmortizationInterestHandlingOptions", ignore = true)
|
||||||
@Mapping(target = "classificationOptions", ignore = true)
|
@Mapping(target = "classificationOptions", ignore = true)
|
||||||
@Mapping(target = "paymentTypeOptions", ignore = true)
|
@Mapping(target = "paymentTypeOptions", ignore = true)
|
||||||
@Mapping(target = "overpaymentPortion", ignore = true)
|
@Mapping(target = "overpaymentPortion", ignore = true)
|
||||||
|
|||||||
+2
@@ -166,4 +166,6 @@ public interface LoanReadPlatformService {
|
|||||||
Long getResolvedLoanTransactionId(Long transactionId, ExternalId externalTransactionId);
|
Long getResolvedLoanTransactionId(Long transactionId, ExternalId externalTransactionId);
|
||||||
|
|
||||||
LoanTransactionData retrieveLoanReAgeTemplate(Long loanId);
|
LoanTransactionData retrieveLoanReAgeTemplate(Long loanId);
|
||||||
|
|
||||||
|
LoanTransactionData retrieveLoanReAmortizationTemplate(Long loanId);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -56,4 +56,5 @@
|
|||||||
<include relativeToChangelogFile="true" file="parts/1031_loan_merchant_buy_down_fee.xml"/>
|
<include relativeToChangelogFile="true" file="parts/1031_loan_merchant_buy_down_fee.xml"/>
|
||||||
<include relativeToChangelogFile="true" file="parts/1032_add_classification_to_loan_transaction.xml"/>
|
<include relativeToChangelogFile="true" file="parts/1032_add_classification_to_loan_transaction.xml"/>
|
||||||
<include relativeToChangelogFile="true" file="parts/1033_add_reage_reasons_for_loan.xml"/>
|
<include relativeToChangelogFile="true" file="parts/1033_add_reage_reasons_for_loan.xml"/>
|
||||||
|
<include relativeToChangelogFile="true" file="parts/1034_loan_reamortization_parameters.xml"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
<?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" objectQuotingStrategy="QUOTE_ALL_OBJECTS">
|
||||||
|
<createTable tableName="m_loan_reamortization_parameter">
|
||||||
|
<column autoIncrement="true" name="id" type="BIGINT">
|
||||||
|
<constraints nullable="false" primaryKey="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="loan_transaction_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column defaultValue="DEFAULT" name="interest_handling_type" type="VARCHAR(40)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column defaultValueComputed="NULL" name="reamortization_reason_code_value_id" type="INT"/>
|
||||||
|
<column name="created_by" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_modified_by" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet id="2-mysql" author="fineract" context="mysql">
|
||||||
|
<addColumn tableName="m_loan_reamortization_parameter">
|
||||||
|
<column name="created_on_utc" type="DATETIME(6)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_modified_on_utc" type="DATETIME(6)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet id="2-postgresql" author="fineract" context="postgresql">
|
||||||
|
<addColumn tableName="m_loan_reamortization_parameter">
|
||||||
|
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="fineract" id="4">
|
||||||
|
<insert tableName="m_code">
|
||||||
|
<column name="code_name" value="ReAmortizationReasons"/>
|
||||||
|
<column name="is_system_defined" valueBoolean="true"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="fineract" id="5">
|
||||||
|
<addForeignKeyConstraint baseColumnNames="reamortization_reason_code_value_id" baseTableName="m_loan_reamortization_parameter"
|
||||||
|
constraintName="FK_reamortization_reason_code_m_code_value" deferrable="false"
|
||||||
|
initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
|
||||||
|
referencedColumnNames="id" referencedTableName="m_code_value" validate="true"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+1
@@ -109,6 +109,7 @@
|
|||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
||||||
|
<class>org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
||||||
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
||||||
|
|||||||
+1
@@ -108,6 +108,7 @@
|
|||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
||||||
|
<class>org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
||||||
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
||||||
|
|||||||
+2
@@ -699,6 +699,8 @@ public class LoanTransactionsApiResource {
|
|||||||
transactionData = this.loanReadPlatformService.retrieveManualInterestRefundTemplate(resolvedLoanId, transactionId);
|
transactionData = this.loanReadPlatformService.retrieveManualInterestRefundTemplate(resolvedLoanId, transactionId);
|
||||||
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.REAGE_COMMAND)) {
|
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.REAGE_COMMAND)) {
|
||||||
transactionData = this.loanReadPlatformService.retrieveLoanReAgeTemplate(resolvedLoanId);
|
transactionData = this.loanReadPlatformService.retrieveLoanReAgeTemplate(resolvedLoanId);
|
||||||
|
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.REAMORTIZATION_COMMAND)) {
|
||||||
|
transactionData = this.loanReadPlatformService.retrieveLoanReAmortizationTemplate(resolvedLoanId);
|
||||||
} else {
|
} else {
|
||||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -132,6 +132,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepayment
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInterestHandlingType;
|
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInterestHandlingType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||||
@@ -2117,6 +2118,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
.reAgeInterestHandlingOptions(LoanReAgeInterestHandlingType.getValuesAsEnumOptionDataList()).build();
|
.reAgeInterestHandlingOptions(LoanReAgeInterestHandlingType.getValuesAsEnumOptionDataList()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoanTransactionData retrieveLoanReAmortizationTemplate(final Long loanId) {
|
||||||
|
final LoanAccountData loan = this.retrieveOne(loanId);
|
||||||
|
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REAMORTIZE);
|
||||||
|
final BigDecimal totalOutstanding = loan.getSummary() != null ? loan.getSummary().getTotalOutstanding() : null;
|
||||||
|
final List<CodeValueData> reAmortizationReasonOptions = new ArrayList<>(
|
||||||
|
codeValueReadPlatformService.retrieveCodeValuesByCode(LoanApiConstants.REAMORTIZATION_REASONS));
|
||||||
|
return LoanTransactionData.builder().type(transactionType).currency(loan.getCurrency()).date(DateUtils.getBusinessLocalDate())
|
||||||
|
.amount(totalOutstanding).netDisbursalAmount(loan.getNetDisbursalAmount()).loanId(loanId)
|
||||||
|
.externalLoanId(loan.getExternalId()).reAmortizationReasonOptions(reAmortizationReasonOptions)
|
||||||
|
.reAmortizationInterestHandlingOptions(LoanReAmortizationInterestHandlingType.getValuesAsEnumOptionDataList()).build();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanTransactionData retrieveLoanChargeOffTemplate(final Long loanId) {
|
public LoanTransactionData retrieveLoanChargeOffTemplate(final Long loanId) {
|
||||||
|
|
||||||
|
|||||||
+24
@@ -26,6 +26,8 @@ import java.util.Comparator;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
|
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
@@ -38,12 +40,15 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
|||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.reamortization.LoanUndoReAmortizeTransactionBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.reamortization.LoanUndoReAmortizeTransactionBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanReAmortizationApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanReAmortizationApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||||
@@ -66,6 +71,7 @@ public class LoanReAmortizationServiceImpl {
|
|||||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||||
private final LoanChargeValidator loanChargeValidator;
|
private final LoanChargeValidator loanChargeValidator;
|
||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
|
private final CodeValueRepository codeValueRepository;
|
||||||
|
|
||||||
public CommandProcessingResult reAmortize(Long loanId, JsonCommand command) {
|
public CommandProcessingResult reAmortize(Long loanId, JsonCommand command) {
|
||||||
Loan loan = loanAssembler.assembleFrom(loanId);
|
Loan loan = loanAssembler.assembleFrom(loanId);
|
||||||
@@ -82,6 +88,8 @@ public class LoanReAmortizationServiceImpl {
|
|||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(reAmortizeTransaction, new TransactionCtx(loan.getCurrency(),
|
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(reAmortizeTransaction, new TransactionCtx(loan.getCurrency(),
|
||||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||||
|
|
||||||
|
reAmortizeTransaction.setLoanReAmortizationParameter(createReAmortizationParameter(reAmortizeTransaction, command));
|
||||||
|
|
||||||
loanTransactionRepository.saveAndFlush(reAmortizeTransaction);
|
loanTransactionRepository.saveAndFlush(reAmortizeTransaction);
|
||||||
|
|
||||||
// delinquency recalculation will be triggered by the event in a decoupled way via a listener
|
// delinquency recalculation will be triggered by the event in a decoupled way via a listener
|
||||||
@@ -159,4 +167,20 @@ public class LoanReAmortizationServiceImpl {
|
|||||||
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.REAMORTIZE, transactionDate, txPrincipalAmount,
|
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.REAMORTIZE, transactionDate, txPrincipalAmount,
|
||||||
txPrincipalAmount, ZERO, ZERO, ZERO, null, false, null, txExternalId);
|
txPrincipalAmount, ZERO, ZERO, ZERO, null, false, null, txExternalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoanReAmortizationParameter createReAmortizationParameter(LoanTransaction reAmortizationTransaction, JsonCommand command) {
|
||||||
|
LoanReAmortizationInterestHandlingType reAmortizationInterestHandlingType = command.enumValueOfParameterNamed(
|
||||||
|
LoanReAmortizationApiConstants.reAmortizationInterestHandlingParamName, LoanReAmortizationInterestHandlingType.class);
|
||||||
|
if (reAmortizationInterestHandlingType == null) {
|
||||||
|
reAmortizationInterestHandlingType = LoanReAmortizationInterestHandlingType.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeValue reasonCodeValue = null;
|
||||||
|
if (command.parameterExists(LoanReAmortizationApiConstants.reasonCodeValueIdParamName)) {
|
||||||
|
reasonCodeValue = codeValueRepository.findByCodeNameAndId(LoanApiConstants.REAMORTIZATION_REASONS,
|
||||||
|
command.longValueOfParameterNamed(LoanReAmortizationApiConstants.reasonCodeValueIdParamName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LoanReAmortizationParameter(reAmortizationTransaction, reAmortizationInterestHandlingType, reasonCodeValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-6
@@ -24,23 +24,31 @@ import java.util.ArrayList;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
|
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanReAmortizationApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanReAmortizationApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ChangeOperation;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ChangeOperation;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class LoanReAmortizationValidator {
|
public class LoanReAmortizationValidator {
|
||||||
|
|
||||||
|
private final CodeValueRepository codeValueRepository;
|
||||||
|
|
||||||
public void validateReAmortize(Loan loan, JsonCommand command) {
|
public void validateReAmortize(Loan loan, JsonCommand command) {
|
||||||
validateReAmortizeRequest(command);
|
validateReAmortizeRequest(command);
|
||||||
validateReAmortizeBusinessRules(loan);
|
validateReAmortizeBusinessRules(loan);
|
||||||
@@ -54,6 +62,23 @@ public class LoanReAmortizationValidator {
|
|||||||
baseDataValidator.reset().parameter(LoanReAmortizationApiConstants.externalIdParameterName).ignoreIfNull().value(externalId)
|
baseDataValidator.reset().parameter(LoanReAmortizationApiConstants.externalIdParameterName).ignoreIfNull().value(externalId)
|
||||||
.notExceedingLengthOf(100);
|
.notExceedingLengthOf(100);
|
||||||
|
|
||||||
|
final LoanReAmortizationInterestHandlingType reAmortizationInterestHandlingType = command.enumValueOfParameterNamed(
|
||||||
|
LoanReAmortizationApiConstants.reAmortizationInterestHandlingParamName, LoanReAmortizationInterestHandlingType.class);
|
||||||
|
baseDataValidator.reset().parameter(LoanReAmortizationApiConstants.reAmortizationInterestHandlingParamName)
|
||||||
|
.value(reAmortizationInterestHandlingType).ignoreIfNull();
|
||||||
|
|
||||||
|
Long reasonCodeValueId = command.longValueOfParameterNamed(LoanReAmortizationApiConstants.reasonCodeValueIdParamName);
|
||||||
|
baseDataValidator.reset().parameter(LoanReAmortizationApiConstants.reasonCodeValueIdParamName).value(reasonCodeValueId)
|
||||||
|
.ignoreIfNull();
|
||||||
|
if (reasonCodeValueId != null) {
|
||||||
|
final CodeValue reasonCodeValue = codeValueRepository.findByCodeNameAndId(LoanApiConstants.REAMORTIZATION_REASONS,
|
||||||
|
reasonCodeValueId);
|
||||||
|
if (reasonCodeValue == null) {
|
||||||
|
dataValidationErrors.add(ApiParameterError.parameterError("validation.msg.reamortization.reason.invalid",
|
||||||
|
"Reamortization Reason with ID " + reasonCodeValueId + " does not exist", LoanApiConstants.REAMORTIZATION_REASONS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throwExceptionIfValidationErrorsExist(dataValidationErrors);
|
throwExceptionIfValidationErrorsExist(dataValidationErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,12 +103,6 @@ public class LoanReAmortizationValidator {
|
|||||||
loan.getId());
|
loan.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate reamortization is only available for non-interest bearing loans
|
|
||||||
if (loan.isInterestBearing()) {
|
|
||||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.supported.only.for.non.interest.loans",
|
|
||||||
"Loan reamortization is only available for non-interest bearing loans", loan.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate reamortization is only done on an active loan
|
// validate reamortization is only done on an active loan
|
||||||
if (!loan.getStatus().isActive()) {
|
if (!loan.getStatus().isActive()) {
|
||||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.supported.only.for.active.loans",
|
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.supported.only.for.active.loans",
|
||||||
|
|||||||
+1
@@ -207,6 +207,7 @@
|
|||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
||||||
|
<class>org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
||||||
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
||||||
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
||||||
|
|||||||
+9
-15
@@ -53,13 +53,21 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
|
|||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.mockito.junit.jupiter.MockitoSettings;
|
||||||
|
import org.mockito.quality.Strictness;
|
||||||
|
|
||||||
@SuppressFBWarnings({ "VA_FORMAT_STRING_USES_NEWLINE" })
|
@SuppressFBWarnings({ "VA_FORMAT_STRING_USES_NEWLINE" })
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||||
class LoanReAmortizationValidatorTest {
|
class LoanReAmortizationValidatorTest {
|
||||||
|
|
||||||
private final LocalDate actualDate = LocalDate.now(Clock.systemUTC());
|
private final LocalDate actualDate = LocalDate.now(Clock.systemUTC());
|
||||||
|
|
||||||
private LoanReAmortizationValidator underTest = new LoanReAmortizationValidator();
|
@InjectMocks
|
||||||
|
private LoanReAmortizationValidator underTest;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@@ -140,20 +148,6 @@ class LoanReAmortizationValidatorTest {
|
|||||||
.isEqualTo("error.msg.loan.reamortize.supported.only.for.progressive.loan.schedule.type");
|
.isEqualTo("error.msg.loan.reamortize.supported.only.for.progressive.loan.schedule.type");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateReAmortize_ShouldThrowException_WhenLoanIsInterestBearing() {
|
|
||||||
// given
|
|
||||||
Loan loan = loan();
|
|
||||||
given(loan.isInterestBearing()).willReturn(true);
|
|
||||||
JsonCommand command = jsonCommand();
|
|
||||||
// when
|
|
||||||
GeneralPlatformDomainRuleException result = assertThrows(GeneralPlatformDomainRuleException.class,
|
|
||||||
() -> underTest.validateReAmortize(loan, command));
|
|
||||||
// then
|
|
||||||
assertThat(result).isNotNull();
|
|
||||||
assertThat(result.getGlobalisationMessageCode()).isEqualTo("error.msg.loan.reamortize.supported.only.for.non.interest.loans");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateReAmortize_ShouldThrowException_WhenLoanIsNotActive() {
|
public void testValidateReAmortize_ShouldThrowException_WhenLoanIsNotActive() {
|
||||||
// given
|
// given
|
||||||
|
|||||||
+2
-1
@@ -1027,8 +1027,9 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
|||||||
loanTransactionHelper.reAge(loanId, request);
|
loanTransactionHelper.reAge(loanId, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void reAmortizeLoan(Long loanId) {
|
protected void reAmortizeLoan(Long loanId, String reAmortizationInterestHandling) {
|
||||||
PostLoansLoanIdTransactionsRequest request = new PostLoansLoanIdTransactionsRequest();
|
PostLoansLoanIdTransactionsRequest request = new PostLoansLoanIdTransactionsRequest();
|
||||||
|
request.setReAmortizationInterestHandling(reAmortizationInterestHandling);
|
||||||
request.setDateFormat(DATETIME_PATTERN);
|
request.setDateFormat(DATETIME_PATTERN);
|
||||||
request.setLocale("en");
|
request.setLocale("en");
|
||||||
loanTransactionHelper.reAmortize(loanId, request);
|
loanTransactionHelper.reAmortize(loanId, request);
|
||||||
|
|||||||
+14
-13
@@ -30,6 +30,7 @@ import org.apache.fineract.client.models.PostLoansResponse;
|
|||||||
import org.apache.fineract.integrationtests.BaseLoanIntegrationTest;
|
import org.apache.fineract.integrationtests.BaseLoanIntegrationTest;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -90,7 +91,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
|
|
||||||
runAt("02 February 2023", () -> {
|
runAt("02 February 2023", () -> {
|
||||||
// create re-amortize transaction
|
// create re-amortize transaction
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
// verify transactions
|
// verify transactions
|
||||||
verifyTransactions(loanId.get(), //
|
verifyTransactions(loanId.get(), //
|
||||||
@@ -157,7 +158,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
|
|
||||||
runAt("02 February 2023", () -> {
|
runAt("02 February 2023", () -> {
|
||||||
// create re-amortize transaction
|
// create re-amortize transaction
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
// verify transactions
|
// verify transactions
|
||||||
verifyTransactions(loanId.get(), //
|
verifyTransactions(loanId.get(), //
|
||||||
@@ -220,7 +221,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
runAt("25 January 2023", () -> {
|
runAt("25 January 2023", () -> {
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -265,7 +266,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
runAt("01 February 2023", () -> {
|
runAt("01 February 2023", () -> {
|
||||||
|
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -319,7 +320,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
runAt("30 January 2023", () -> {
|
runAt("30 January 2023", () -> {
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -375,7 +376,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
runAt("31 January 2023", () -> {
|
runAt("31 January 2023", () -> {
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -421,7 +422,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
runAt("01 February 2023", () -> {
|
runAt("01 February 2023", () -> {
|
||||||
addCharge(loanId.get(), false, 10.0, "27 February 2023");
|
addCharge(loanId.get(), false, 10.0, "27 February 2023");
|
||||||
|
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -466,7 +467,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
runAt("01 February 2023", () -> {
|
runAt("01 February 2023", () -> {
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -549,7 +550,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
runAt("01 February 2023", () -> {
|
runAt("01 February 2023", () -> {
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -613,7 +614,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
runAt("01 February 2023", () -> {
|
runAt("01 February 2023", () -> {
|
||||||
|
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -677,7 +678,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
runAt("17 January 2023", () -> {
|
runAt("17 January 2023", () -> {
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -726,7 +727,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
runAt("25 January 2023", () -> {
|
runAt("25 January 2023", () -> {
|
||||||
|
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
@@ -821,7 +822,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
runAt("25 January 2023", () -> {
|
runAt("25 January 2023", () -> {
|
||||||
|
|
||||||
reAmortizeLoan(loanId.get());
|
reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
|
||||||
|
|
||||||
verifyRepaymentSchedule(loanId.get(), //
|
verifyRepaymentSchedule(loanId.get(), //
|
||||||
installment(500, null, "01 January 2023"), //
|
installment(500, null, "01 January 2023"), //
|
||||||
|
|||||||
Reference in New Issue
Block a user