diff --git a/fineract-investor/src/main/resources/jpa/static-weaving/module/fineract-investor/persistence.xml b/fineract-investor/src/main/resources/jpa/static-weaving/module/fineract-investor/persistence.xml
index 656e3f3ea..3daf8ed78 100644
--- a/fineract-investor/src/main/resources/jpa/static-weaving/module/fineract-investor/persistence.xml
+++ b/fineract-investor/src/main/resources/jpa/static-weaving/module/fineract-investor/persistence.xml
@@ -120,6 +120,7 @@
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping
org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter
+ org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory
org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest
org.apache.fineract.portfolio.loanproduct.domain.LoanProduct
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
index 2bd8b800d..fa651a64c 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
@@ -142,6 +142,8 @@ public interface LoanApiConstants {
String CHARGE_OFF_REASONS = "ChargeOffReasons";
// loan ReAge
String REAGE_REASONS = "ReAgeReasons";
+ // loan ReAmortization
+ String REAMORTIZATION_REASONS = "ReAmortizationReasons";
// fore closure constants
String transactionDateParamName = "transactionDate";
String noteParamName = "note";
@@ -186,6 +188,7 @@ public interface LoanApiConstants {
String BUY_DOWN_FEE_COMMAND = "buyDownFee";
String BUY_DOWN_FEE_ADJUSTMENT_COMMAND = "buyDownFeeAdjustment";
String REAGE_COMMAND = "reAge";
+ String REAMORTIZATION_COMMAND = "reAmortization";
// Data Validator names
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanReAmortizationApiConstants.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanReAmortizationApiConstants.java
index 5ef37442e..256513268 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanReAmortizationApiConstants.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanReAmortizationApiConstants.java
@@ -23,4 +23,7 @@ public interface LoanReAmortizationApiConstants {
String localeParameterName = "locale";
String dateFormatParameterName = "dateFormat";
String externalIdParameterName = "externalId";
+
+ String reAmortizationInterestHandlingParamName = "reAmortizationInterestHandling";
+ String reasonCodeValueIdParamName = "reasonCodeValueId";
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
index 324d905e1..205d1b833 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
@@ -370,6 +370,8 @@ final class LoanTransactionsApiResourceSwagger {
public Integer numberOfInstallments;
@Schema(example = "DEFAULT")
public String reAgeInterestHandling;
+ @Schema(example = "DEFAULT")
+ public String reAmortizationInterestHandling;
@Schema(example = "1")
public Long reasonCodeValueId;
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
index 715af39a8..04d6a25ee 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
@@ -121,6 +121,8 @@ public class LoanTransactionData implements Serializable {
private Collection reAgeReasonOptions = null;
private Collection periodFrequencyOptions = null;
private Collection reAgeInterestHandlingOptions = null;
+ private Collection reAmortizationReasonOptions = null;
+ private Collection reAmortizationInterestHandlingOptions = null;
public static LoanTransactionData importInstance(BigDecimal repaymentAmount, LocalDate lastRepaymentDate, Long repaymentTypeId,
Integer rowIndex, String locale, String dateFormat) {
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index f77a490ae..1490462f7 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -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.office.domain.Office;
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.paymentdetail.domain.PaymentDetail;
@@ -145,6 +146,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom 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());
+ }
+
+}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/reamortization/LoanReAmortizationParameter.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/reamortization/LoanReAmortizationParameter.java
new file mode 100644
index 000000000..bac04341a
--- /dev/null
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/reamortization/LoanReAmortizationParameter.java
@@ -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 {
+
+ @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() {}
+}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTransactionMapper.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTransactionMapper.java
index 4aa9a0451..9682a4df3 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTransactionMapper.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTransactionMapper.java
@@ -34,8 +34,10 @@ public interface LoanTransactionMapper {
@Mapping(target = "writeOffReasonOptions", ignore = true)
@Mapping(target = "chargeOffReasonOptions", ignore = true)
@Mapping(target = "reAgeReasonOptions", ignore = true)
+ @Mapping(target = "reAmortizationReasonOptions", ignore = true)
@Mapping(target = "periodFrequencyOptions", ignore = true)
@Mapping(target = "reAgeInterestHandlingOptions", ignore = true)
+ @Mapping(target = "reAmortizationInterestHandlingOptions", ignore = true)
@Mapping(target = "classificationOptions", ignore = true)
@Mapping(target = "paymentTypeOptions", ignore = true)
@Mapping(target = "overpaymentPortion", ignore = true)
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
index 29773944a..f8c5e0294 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
@@ -166,4 +166,6 @@ public interface LoanReadPlatformService {
Long getResolvedLoanTransactionId(Long transactionId, ExternalId externalTransactionId);
LoanTransactionData retrieveLoanReAgeTemplate(Long loanId);
+
+ LoanTransactionData retrieveLoanReAmortizationTemplate(Long loanId);
}
diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
index b319dc40e..e086e3124 100644
--- a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
+++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
@@ -56,4 +56,5 @@
+
diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1034_loan_reamortization_parameters.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1034_loan_reamortization_parameters.xml
new file mode 100644
index 000000000..b1f3ad9ce
--- /dev/null
+++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1034_loan_reamortization_parameters.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fineract-loan/src/main/resources/jpa/static-weaving/module/fineract-loan/persistence.xml b/fineract-loan/src/main/resources/jpa/static-weaving/module/fineract-loan/persistence.xml
index d749153be..edc5b8c27 100644
--- a/fineract-loan/src/main/resources/jpa/static-weaving/module/fineract-loan/persistence.xml
+++ b/fineract-loan/src/main/resources/jpa/static-weaving/module/fineract-loan/persistence.xml
@@ -109,6 +109,7 @@
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping
org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter
+ org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory
org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest
org.apache.fineract.portfolio.loanproduct.domain.LoanProduct
diff --git a/fineract-progressive-loan/src/main/resources/jpa/static-weaving/module/fineract-progressive-loan/persistence.xml b/fineract-progressive-loan/src/main/resources/jpa/static-weaving/module/fineract-progressive-loan/persistence.xml
index 72a418ca9..33d37d489 100644
--- a/fineract-progressive-loan/src/main/resources/jpa/static-weaving/module/fineract-progressive-loan/persistence.xml
+++ b/fineract-progressive-loan/src/main/resources/jpa/static-weaving/module/fineract-progressive-loan/persistence.xml
@@ -108,6 +108,7 @@
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping
org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter
+ org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory
org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest
org.apache.fineract.portfolio.loanproduct.domain.LoanProduct
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
index ac85e4b43..a3627c9ac 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
@@ -699,6 +699,8 @@ public class LoanTransactionsApiResource {
transactionData = this.loanReadPlatformService.retrieveManualInterestRefundTemplate(resolvedLoanId, transactionId);
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.REAGE_COMMAND)) {
transactionData = this.loanReadPlatformService.retrieveLoanReAgeTemplate(resolvedLoanId);
+ } else if (CommandParameterUtil.is(commandParam, LoanApiConstants.REAMORTIZATION_COMMAND)) {
+ transactionData = this.loanReadPlatformService.retrieveLoanReAmortizationTemplate(resolvedLoanId);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 28d360460..94e1cc8e7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -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.LoanTransactionType;
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.LoanTransactionNotFoundException;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
@@ -2117,6 +2118,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
.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 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
public LoanTransactionData retrieveLoanChargeOffTemplate(final Long loanId) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationServiceImpl.java
index 223dc4fdd..a64e324a7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationServiceImpl.java
@@ -26,6 +26,8 @@ import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
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.data.CommandProcessingResult;
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.service.BusinessEventNotifierService;
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.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
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.MoneyHolder;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
@@ -66,6 +71,7 @@ public class LoanReAmortizationServiceImpl {
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
private final LoanChargeValidator loanChargeValidator;
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
+ private final CodeValueRepository codeValueRepository;
public CommandProcessingResult reAmortize(Long loanId, JsonCommand command) {
Loan loan = loanAssembler.assembleFrom(loanId);
@@ -82,6 +88,8 @@ public class LoanReAmortizationServiceImpl {
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(reAmortizeTransaction, new TransactionCtx(loan.getCurrency(),
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
+ reAmortizeTransaction.setLoanReAmortizationParameter(createReAmortizationParameter(reAmortizeTransaction, command));
+
loanTransactionRepository.saveAndFlush(reAmortizeTransaction);
// 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,
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);
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidator.java
index 58ad2fb7c..3e4888908 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidator.java
@@ -24,23 +24,31 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
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.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
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.domain.Loan;
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.ChangeOperation;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.springframework.stereotype.Component;
@Component
+@RequiredArgsConstructor
public class LoanReAmortizationValidator {
+ private final CodeValueRepository codeValueRepository;
+
public void validateReAmortize(Loan loan, JsonCommand command) {
validateReAmortizeRequest(command);
validateReAmortizeBusinessRules(loan);
@@ -54,6 +62,23 @@ public class LoanReAmortizationValidator {
baseDataValidator.reset().parameter(LoanReAmortizationApiConstants.externalIdParameterName).ignoreIfNull().value(externalId)
.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);
}
@@ -78,12 +103,6 @@ public class LoanReAmortizationValidator {
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
if (!loan.getStatus().isActive()) {
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.supported.only.for.active.loans",
diff --git a/fineract-provider/src/main/resources/jpa/static-weaving/module/fineract-provider/persistence.xml b/fineract-provider/src/main/resources/jpa/static-weaving/module/fineract-provider/persistence.xml
index 7c7b5dd9c..f3998cc5a 100644
--- a/fineract-provider/src/main/resources/jpa/static-weaving/module/fineract-provider/persistence.xml
+++ b/fineract-provider/src/main/resources/jpa/static-weaving/module/fineract-provider/persistence.xml
@@ -207,6 +207,7 @@
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping
org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter
+ org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory
org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest
org.apache.fineract.portfolio.loanproduct.domain.LoanProduct
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidatorTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidatorTest.java
index baa24ee4a..d84219a47 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidatorTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidatorTest.java
@@ -53,13 +53,21 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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" })
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
class LoanReAmortizationValidatorTest {
private final LocalDate actualDate = LocalDate.now(Clock.systemUTC());
- private LoanReAmortizationValidator underTest = new LoanReAmortizationValidator();
+ @InjectMocks
+ private LoanReAmortizationValidator underTest;
@BeforeEach
public void setUp() {
@@ -140,20 +148,6 @@ class LoanReAmortizationValidatorTest {
.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
public void testValidateReAmortize_ShouldThrowException_WhenLoanIsNotActive() {
// given
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index e8daf2906..4bf242ae1 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -1027,8 +1027,9 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
loanTransactionHelper.reAge(loanId, request);
}
- protected void reAmortizeLoan(Long loanId) {
+ protected void reAmortizeLoan(Long loanId, String reAmortizationInterestHandling) {
PostLoansLoanIdTransactionsRequest request = new PostLoansLoanIdTransactionsRequest();
+ request.setReAmortizationInterestHandling(reAmortizationInterestHandling);
request.setDateFormat(DATETIME_PATTERN);
request.setLocale("en");
loanTransactionHelper.reAmortize(loanId, request);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/reamortization/LoanReAmortizationIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/reamortization/LoanReAmortizationIntegrationTest.java
index 2df5de656..b6ac4b30a 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/reamortization/LoanReAmortizationIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/reamortization/LoanReAmortizationIntegrationTest.java
@@ -30,6 +30,7 @@ import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.integrationtests.BaseLoanIntegrationTest;
import org.apache.fineract.integrationtests.common.ClientHelper;
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.LoanScheduleType;
import org.junit.jupiter.api.Test;
@@ -90,7 +91,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
runAt("02 February 2023", () -> {
// create re-amortize transaction
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
// verify transactions
verifyTransactions(loanId.get(), //
@@ -157,7 +158,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
runAt("02 February 2023", () -> {
// create re-amortize transaction
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
// verify transactions
verifyTransactions(loanId.get(), //
@@ -220,7 +221,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
);
});
runAt("25 January 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -265,7 +266,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
});
runAt("01 February 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -319,7 +320,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
);
});
runAt("30 January 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -375,7 +376,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
);
});
runAt("31 January 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -421,7 +422,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
runAt("01 February 2023", () -> {
addCharge(loanId.get(), false, 10.0, "27 February 2023");
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -466,7 +467,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
);
});
runAt("01 February 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -549,7 +550,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
);
});
runAt("01 February 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -613,7 +614,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
});
runAt("01 February 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -677,7 +678,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
);
});
runAt("17 January 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -726,7 +727,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
});
runAt("25 January 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //
@@ -821,7 +822,7 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
});
runAt("25 January 2023", () -> {
- reAmortizeLoan(loanId.get());
+ reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name());
verifyRepaymentSchedule(loanId.get(), //
installment(500, null, "01 January 2023"), //