This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 39a8d5c63e FINERACT-2181: Installment amount in multiples of should be 
inherited from loan product to loan level
39a8d5c63e is described below

commit 39a8d5c63e2763bacdcfee132d89289e44f2a88c
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Sat May 31 08:50:52 2025 -0500

    FINERACT-2181: Installment amount in multiples of should be inherited from 
loan product to loan level
---
 .../loanschedule/domain/LoanApplicationTerms.java  |  2 +-
 .../mapper/LoanTermVariationsMapper.java           |  5 ++-
 .../service/LoanDownPaymentHandlerServiceImpl.java |  5 ++-
 .../portfolio/loanproduct/domain/LoanProduct.java  |  7 +---
 .../domain/LoanProductRelatedDetail.java           | 12 ++++--
 .../tenant/module/loan/module-changelog-master.xml |  1 +
 ..._installment_amount_in_multiples_of_to_loan.xml | 46 ++++++++++++++++++++++
 ...dvancedPaymentScheduleTransactionProcessor.java | 15 ++++---
 ...InterestScheduleModelRepositoryWrapperImpl.java |  2 +-
 .../InternalProgressiveLoanApiResource.java        |  2 +-
 .../service/LoanScheduleAssembler.java             | 15 ++++---
 .../loanaccount/service/LoanAssemblerImpl.java     |  6 +--
 .../loanaccount/service/LoanProductUpdateUtil.java |  4 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |  4 +-
 .../LoanProductReadPlatformServiceImpl.java        |  2 +-
 .../LoanDownPaymentHandlerServiceImplTest.java     |  2 +-
 16 files changed, 94 insertions(+), 36 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
index 2c2cad5bbc..308cc55edf 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -1616,7 +1616,7 @@ public final class LoanApplicationTerms {
                 this.loanScheduleProcessingType, this.fixedLength, 
this.enableAccrualActivityPosting, this.supportedInterestRefundTypes,
                 this.chargeOffBehaviour, 
this.interestRecognitionOnDisbursementDate, this.daysInYearCustomStrategy,
                 this.enableIncomeCapitalization, 
this.capitalizedIncomeCalculationType, this.capitalizedIncomeStrategy,
-                this.capitalizedIncomeType);
+                this.capitalizedIncomeType, 
this.installmentAmountInMultiplesOf);
     }
 
     public LoanProductMinimumRepaymentScheduleRelatedDetail 
toLoanProductRelatedDetailMinimumData() {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTermVariationsMapper.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTermVariationsMapper.java
index a11480a64c..6d21260b50 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTermVariationsMapper.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTermVariationsMapper.java
@@ -112,8 +112,9 @@ public class LoanTermVariationsMapper {
                 
scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), 
loan.getInArrearsTolerance(),
                 loan.getLoanRepaymentScheduleDetail(), 
loan.getLoanProduct().isMultiDisburseLoan(), loan.getFixedEmiAmount(),
                 disbursementData, loan.getMaxOutstandingLoanBalance(), 
interestChargedFromDate,
-                
loan.getLoanProduct().getPrincipalThresholdForLastInstallment(), 
loan.getLoanProduct().getInstallmentAmountInMultiplesOf(),
-                recalculationFrequencyType, restCalendarInstance, 
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
+                
loan.getLoanProduct().getPrincipalThresholdForLastInstallment(),
+                
loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf(), 
recalculationFrequencyType, restCalendarInstance,
+                compoundingMethod, compoundingCalendarInstance, 
compoundingFrequencyType,
                 loan.getLoanProduct().preCloseInterestCalculationStrategy(), 
rescheduleStrategyMethod, calendar,
                 loan.getApprovedPrincipal(), annualNominalInterestRate, 
loanTermVariations, calendarHistoryDataWrapper,
                 scheduleGeneratorDTO.getNumberOfdays(), 
scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), holidayDetailDTO,
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java
index 621bad353a..5e2004790f 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java
@@ -197,8 +197,9 @@ public class LoanDownPaymentHandlerServiceImpl implements 
LoanDownPaymentHandler
         }
         Money downPaymentMoney = Money.of(loan.getCurrency(),
                 MathUtil.percentageOf(disbursementTransaction.getAmount(), 
disbursedAmountPercentageForDownPayment, 19));
-        if (loan.getLoanProduct().getInstallmentAmountInMultiplesOf() != null) 
{
-            downPaymentMoney = Money.roundToMultiplesOf(downPaymentMoney, 
loan.getLoanProduct().getInstallmentAmountInMultiplesOf());
+        if 
(loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf() != 
null) {
+            downPaymentMoney = Money.roundToMultiplesOf(downPaymentMoney,
+                    
loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf());
         }
         final Money adjustedDownPaymentMoney = switch 
(loan.getLoanProductRelatedDetail().getLoanScheduleType()) {
             // For Cumulative loan: To check whether the loan was overpaid 
when the disbursement happened and to get the
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
index d9f7dbd713..f3dae22e9e 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
@@ -179,9 +179,6 @@ public class LoanProduct extends 
AbstractPersistableCustom<Long> {
     @Column(name = "can_define_fixed_emi_amount")
     private boolean canDefineInstallmentAmount;
 
-    @Column(name = "instalment_amount_in_multiples_of")
-    private Integer installmentAmountInMultiplesOf;
-
     @Column(name = "is_linked_to_floating_interest_rates", nullable = false)
     private boolean isLinkedToFloatingInterestRate;
 
@@ -336,7 +333,8 @@ public class LoanProduct extends 
AbstractPersistableCustom<Long> {
                 isInterestRecalculationEnabled, isEqualAmortization, 
enableDownPayment, disbursedAmountPercentageForDownPayment,
                 enableAutoRepaymentForDownPayment, loanScheduleType, 
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting,
                 supportedInterestRefundTypes, chargeOffBehaviour, 
isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy,
-                enableIncomeCapitalization, capitalizedIncomeCalculationType, 
capitalizedIncomeStrategy, capitalizedIncomeType);
+                enableIncomeCapitalization, capitalizedIncomeCalculationType, 
capitalizedIncomeStrategy, capitalizedIncomeType,
+                installmentAmountInMultiplesOf);
 
         this.loanProductMinMaxConstraints = new 
LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
                 defaultMinNominalInterestRatePerPeriod, 
defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
@@ -368,7 +366,6 @@ public class LoanProduct extends 
AbstractPersistableCustom<Long> {
         this.principalThresholdForLastInstallment = 
principalThresholdForLastInstallment;
         this.accountMovesOutOfNPAOnlyOnArrearsCompletion = 
accountMovesOutOfNPAOnlyOnArrearsCompletion;
         this.canDefineInstallmentAmount = canDefineEmiAmount;
-        this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
         this.syncExpectedWithDisbursementDate = 
syncExpectedWithDisbursementDate;
         this.canUseForTopup = canUseForTopup;
         this.fixedPrincipalPercentagePerInstallment = 
fixedPrincipalPercentagePerInstallment;
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
index 6ca788655e..e0723e8a53 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
@@ -186,6 +186,9 @@ public class LoanProductRelatedDetail implements 
LoanProductMinimumRepaymentSche
     @Column(name = "capitalized_income_type")
     private LoanCapitalizedIncomeType capitalizedIncomeType;
 
+    @Column(name = "installment_amount_in_multiples_of")
+    private Integer installmentAmountInMultiplesOf;
+
     public static LoanProductRelatedDetail createFrom(final CurrencyData 
currencyData, final BigDecimal principal,
             final BigDecimal nominalInterestRatePerPeriod, final 
PeriodFrequencyType interestRatePeriodFrequencyType,
             final BigDecimal nominalAnnualInterestRate, final InterestMethod 
interestMethod,
@@ -202,7 +205,8 @@ public class LoanProductRelatedDetail implements 
LoanProductMinimumRepaymentSche
             final LoanChargeOffBehaviour chargeOffBehaviour, final boolean 
interestRecognitionOnDisbursementDate,
             final DaysInYearCustomStrategyType daysInYearCustomStrategy, final 
boolean enableIncomeCapitalization,
             final LoanCapitalizedIncomeCalculationType 
capitalizedIncomeCalculationType,
-            final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, 
final LoanCapitalizedIncomeType capitalizedIncomeType) {
+            final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, 
final LoanCapitalizedIncomeType capitalizedIncomeType,
+            final Integer installmentAmountInMultiplesOf) {
 
         final MonetaryCurrency currency = 
MonetaryCurrency.fromCurrencyData(currencyData);
         return new LoanProductRelatedDetail(currency, principal, 
nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
@@ -213,7 +217,7 @@ public class LoanProductRelatedDetail implements 
LoanProductMinimumRepaymentSche
                 isEqualAmortization, enableDownPayment, 
disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
                 loanScheduleType, loanScheduleProcessingType, fixedLength, 
enableAccrualActivityPosting, supportedInterestRefundTypes,
                 chargeOffBehaviour, interestRecognitionOnDisbursementDate, 
daysInYearCustomStrategy, enableIncomeCapitalization,
-                capitalizedIncomeCalculationType, capitalizedIncomeStrategy, 
capitalizedIncomeType);
+                capitalizedIncomeCalculationType, capitalizedIncomeStrategy, 
capitalizedIncomeType, installmentAmountInMultiplesOf);
     }
 
     protected LoanProductRelatedDetail() {
@@ -236,7 +240,8 @@ public class LoanProductRelatedDetail implements 
LoanProductMinimumRepaymentSche
             final LoanChargeOffBehaviour chargeOffBehaviour, final boolean 
interestRecognitionOnDisbursementDate,
             final DaysInYearCustomStrategyType daysInYearCustomStrategy, final 
boolean enableIncomeCapitalization,
             final LoanCapitalizedIncomeCalculationType 
capitalizedIncomeCalculationType,
-            final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, 
final LoanCapitalizedIncomeType capitalizedIncomeType) {
+            final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, 
final LoanCapitalizedIncomeType capitalizedIncomeType,
+            final Integer installmentAmountInMultiplesOf) {
         this.currency = currency;
         this.principal = defaultPrincipal;
         this.nominalInterestRatePerPeriod = 
defaultNominalInterestRatePerPeriod;
@@ -278,6 +283,7 @@ public class LoanProductRelatedDetail implements 
LoanProductMinimumRepaymentSche
         this.capitalizedIncomeCalculationType = 
capitalizedIncomeCalculationType;
         this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
         this.capitalizedIncomeType = capitalizedIncomeType;
+        this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
     }
 
     private Integer defaultToNullIfZero(final Integer value) {
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 89230b66ea..50364a23aa 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
@@ -51,4 +51,5 @@
   <include relativeToChangelogFile="true" 
file="parts/1026_add_interest_recognition_flag.xml"/>
   <include relativeToChangelogFile="true" 
file="parts/1027_add_capitalized_income_transaction_type.xml"/>
   <include relativeToChangelogFile="true" 
file="parts/1028_add_missing_indexes.xml"/>
+  <include relativeToChangelogFile="true" 
file="parts/1029_add_installment_amount_in_multiples_of_to_loan.xml"/>
 </databaseChangeLog>
diff --git 
a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1029_add_installment_amount_in_multiples_of_to_loan.xml
 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1029_add_installment_amount_in_multiples_of_to_loan.xml
new file mode 100644
index 0000000000..c36d41c798
--- /dev/null
+++ 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1029_add_installment_amount_in_multiples_of_to_loan.xml
@@ -0,0 +1,46 @@
+<?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="1029-1">
+        <addColumn tableName="m_loan">
+            <column defaultValueComputed="NULL" 
name="installment_amount_in_multiples_of" type="DECIMAL(19, 6)"/>
+        </addColumn>
+    </changeSet>
+    <changeSet author="fineract" id="1029-2" 
objectQuotingStrategy="QUOTE_ALL_OBJECTS">
+        <renameColumn tableName="m_product_loan" 
oldColumnName="instalment_amount_in_multiples_of" 
newColumnName="installment_amount_in_multiples_of" columnDataType="DECIMAL(19, 
6)"/>
+    </changeSet>
+    <changeSet author="fineract" id="1029-3-postgresql" context="postgresql">
+        <sql>
+            UPDATE m_loan SET installment_amount_in_multiples_of = 
lp.installment_amount_in_multiples_of FROM (
+                SELECT id, installment_amount_in_multiples_of FROM 
m_product_loan
+            ) lp WHERE lp.id = m_loan.product_id
+        </sql>
+    </changeSet>
+    <changeSet author="fineract" id="1029-3-mysql" context="mysql">
+        <sql>
+            UPDATE m_loan l inner join m_product_loan lp on lp.id = 
l.product_id set l.installment_amount_in_multiples_of = 
lp.installment_amount_in_multiples_of
+        </sql>
+    </changeSet>
+</databaseChangeLog>
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index bd36229754..9ffe5c0a4e 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -215,7 +215,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         final Loan loan = loanTransactions.get(0).getLoan();
         List<LoanTermVariationsData> loanTermVariations = 
loan.getActiveLoanTermVariations().stream().map(LoanTermVariations::toData)
                 .collect(Collectors.toCollection(ArrayList::new));
-        final Integer installmentAmountInMultiplesOf = 
loan.getLoanProduct().getInstallmentAmountInMultiplesOf();
+        final Integer installmentAmountInMultiplesOf = 
loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf();
         final LoanProductRelatedDetail loanProductRelatedDetail = 
loan.getLoanRepaymentScheduleDetail();
         ProgressiveLoanInterestScheduleModel scheduleModel = 
emiCalculator.generateInstallmentInterestScheduleModel(installments,
                 loanProductRelatedDetail, loanTermVariations, 
installmentAmountInMultiplesOf, overpaymentHolder.getMoneyObject().getMc());
@@ -464,7 +464,8 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                 
.max(Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate)).get();
         BigDecimal reAmortizationAmountPerInstallment = 
overallOverDuePrincipal.divide(BigDecimal.valueOf(futureInstallments.size()),
                 MoneyHelper.getRoundingMode());
-        Integer installmentAmountInMultiplesOf = 
loanTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf();
+        Integer installmentAmountInMultiplesOf = 
loanTransaction.getLoan().getLoanProductRelatedDetail()
+                .getInstallmentAmountInMultiplesOf();
 
         for (LoanRepaymentScheduleInstallment installment : 
futureInstallments) {
             if (lastFutureInstallment.equals(installment)) {
@@ -1225,7 +1226,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         final MathContext mc = MoneyHelper.getMathContext();
         Loan loan = disbursementTransaction.getLoan();
         LoanProductRelatedDetail loanProductRelatedDetail = 
loan.getLoanRepaymentScheduleDetail();
-        Integer installmentAmountInMultiplesOf = 
loan.getLoanProduct().getInstallmentAmountInMultiplesOf();
+        Integer installmentAmountInMultiplesOf = 
loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf();
         List<LoanRepaymentScheduleInstallment> installments = 
transactionCtx.getInstallments();
         LocalDate transactionDate = 
disbursementTransaction.getTransactionDate();
         MonetaryCurrency currency = transactionCtx.getCurrency();
@@ -1259,7 +1260,8 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                 i -> 
i.getDueDate().isAfter(disbursementTransaction.getTransactionDate()) && 
!i.isDownPayment() && !i.isAdditional())
                 .toList();
         LoanProductRelatedDetail loanProductRelatedDetail = 
disbursementTransaction.getLoan().getLoanRepaymentScheduleDetail();
-        Integer installmentAmountInMultiplesOf = 
disbursementTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf();
+        Integer installmentAmountInMultiplesOf = 
disbursementTransaction.getLoan().getLoanProductRelatedDetail()
+                .getInstallmentAmountInMultiplesOf();
         Money downPaymentAmount = Money.zero(currency);
         if (loanProductRelatedDetail.isEnableDownPayment()) {
             LoanRepaymentScheduleInstallment downPaymentInstallment = 
installments.stream()
@@ -1311,7 +1313,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments 
= installments.stream().filter(
                 i -> 
i.getDueDate().isAfter(capitalizedIncomeTransaction.getTransactionDate()) && 
!i.isDownPayment() && !i.isAdditional())
                 .toList();
-        Integer installmentAmountInMultiplesOf = 
capitalizedIncomeTransaction.getLoan().getLoanProduct()
+        Integer installmentAmountInMultiplesOf = 
capitalizedIncomeTransaction.getLoan().getLoanProduct().getLoanProductRelatedDetail()
                 .getInstallmentAmountInMultiplesOf();
 
         Money amortizableAmount = 
capitalizedIncomeTransaction.getAmount(currency);
@@ -2622,7 +2624,8 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         if (outstandingPrincipalBalance.get().isGreaterThanZero()) {
             calculatedPrincipal = outstandingPrincipalBalance.get()
                     
.dividedBy(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments(), 
MoneyHelper.getMathContext());
-            Integer installmentAmountInMultiplesOf = 
loanTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf();
+            Integer installmentAmountInMultiplesOf = 
loanTransaction.getLoan().getLoanProductRelatedDetail()
+                    .getInstallmentAmountInMultiplesOf();
             if (installmentAmountInMultiplesOf != null) {
                 calculatedPrincipal = 
Money.roundToMultiplesOf(calculatedPrincipal, installmentAmountInMultiplesOf);
             }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java
index 261e5d25d6..2aabbaa89d 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java
@@ -83,7 +83,7 @@ public class InterestScheduleModelRepositoryWrapperImpl 
implements InterestSched
         return progressiveLoanModel.map(ProgressiveLoanModel::getJsonModel) //
                 .map(jsonModel -> 
progressiveLoanInterestScheduleModelParserService.fromJson(jsonModel,
                         
progressiveLoanModel.get().getLoan().getLoanProductRelatedDetail(), 
MoneyHelper.getMathContext(),
-                        
progressiveLoanModel.get().getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf()));
+                        
progressiveLoanModel.get().getLoan().getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf()));
     }
 
     @Override
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java
index d2a59a2623..f7bbec6356 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java
@@ -83,7 +83,7 @@ public class InternalProgressiveLoanApiResource implements 
InitializingBean {
         }
 
         return 
writePlatformService.readProgressiveLoanInterestScheduleModel(loanId, 
loan.getLoanRepaymentScheduleDetail(),
-                
loan.getLoanProduct().getInstallmentAmountInMultiplesOf()).orElse(null);
+                
loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf()).orElse(null);
     }
 
     private ProgressiveLoanInterestScheduleModel 
reprocessTransactionsAndGetModel(final Loan loan) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
index 5a87e0e908..1e22e540c8 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -432,8 +432,6 @@ public class LoanScheduleAssembler {
 
         final BigDecimal principalThresholdForLastInstalment = 
loanProduct.getPrincipalThresholdForLastInstallment();
 
-        final Integer installmentAmountInMultiplesOf = 
loanProduct.getInstallmentAmountInMultiplesOf();
-
         List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
         if (loanProduct.isLinkedToFloatingInterestRate()) {
             final BigDecimal interestRateDiff = this.fromApiJsonHelper
@@ -538,8 +536,9 @@ public class LoanScheduleAssembler {
                 loanProduct.isMultiDisburseLoan(), emiAmount, 
disbursementDatas, maxOutstandingBalance, graceOnArrearsAgeing,
                 daysInMonthType, daysInYearType, 
isInterestRecalculationEnabled, recalculationFrequencyType, 
restCalendarInstance,
                 compoundingMethod, compoundingCalendarInstance, 
compoundingFrequencyType, principalThresholdForLastInstalment,
-                installmentAmountInMultiplesOf, 
loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO,
-                loanTermVariations, 
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, 
isSkipMeetingOnFirstDay, detailDTO,
+                
loanProduct.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf(),
+                loanProduct.preCloseInterestCalculationStrategy(), calendar, 
BigDecimal.ZERO, loanTermVariations,
+                isInterestChargedFromDateSameAsDisbursalDateEnabled, 
numberOfDays, isSkipMeetingOnFirstDay, detailDTO,
                 allowCompoundingOnEod, isEqualAmortization, 
isInterestToBeRecoveredFirstWhenGreaterThanEMI,
                 fixedPrincipalPercentagePerInstallment, 
isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled,
                 disbursedAmountPercentageForDownPayment, 
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
@@ -673,8 +672,8 @@ public class LoanScheduleAssembler {
         }
     }
 
-    public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final 
JsonElement element, final LoanProduct loanProduct) {
-        final LoanApplicationTerms loanApplicationTerms = 
assembleLoanApplicationTermsFrom(element, loanProduct);
+    public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final 
LoanApplicationTerms loanApplicationTerms,
+            final JsonElement element) {
         LoanProductRelatedDetail loanProductRelatedDetail = 
loanApplicationTerms.toLoanProductRelatedDetail();
         final String interestRateFrequencyTypeParamName = 
"interestRateFrequencyType";
         if 
(this.fromApiJsonHelper.parameterExists(interestRateFrequencyTypeParamName, 
element)) {
@@ -684,6 +683,10 @@ public class LoanScheduleAssembler {
         return loanProductRelatedDetail;
     }
 
+    public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final 
JsonElement element, final LoanProduct loanProduct) {
+        return 
assembleLoanProductRelatedDetail(assembleLoanApplicationTermsFrom(element, 
loanProduct), element);
+    }
+
     public LoanScheduleModel assembleLoanScheduleFrom(final JsonElement 
element) {
         // This method is getting called from calculate loan schedule.
         final LoanApplicationTerms loanApplicationTerms = 
assembleLoanTerms(element);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
index a6e6ebc3d0..2725325678 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
@@ -221,8 +221,9 @@ public class LoanAssemblerImpl implements LoanAssembler {
         // Here we add Rates to LoanApplication
         final List<Rate> rates = this.rateAssembler.fromParsedJson(element);
 
-        final LoanProductRelatedDetail loanProductRelatedDetail = 
this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element,
-                loanProduct);
+        final LoanApplicationTerms loanApplicationTerms = 
this.loanScheduleAssembler.assembleLoanTerms(element);
+        final LoanProductRelatedDetail loanProductRelatedDetail = 
this.loanScheduleAssembler
+                .assembleLoanProductRelatedDetail(loanApplicationTerms, 
element);
 
         final BigDecimal interestRateDifferential = this.fromApiJsonHelper
                 
.extractBigDecimalWithLocaleNamed(LoanApiConstants.interestRateDifferentialParameterName,
 element);
@@ -247,7 +248,6 @@ public class LoanAssemblerImpl implements LoanAssembler {
             isEnableInstallmentLevelDelinquency = 
loanProduct.isEnableInstallmentLevelDelinquency();
         }
 
-        final LoanApplicationTerms loanApplicationTerms = 
this.loanScheduleAssembler.assembleLoanTerms(element);
         final boolean isHolidayEnabled = 
this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
         Long officeId = client != null ? client.getOffice().getId() : 
group.getOffice().getId();
         final List<Holiday> holidays = 
this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductUpdateUtil.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductUpdateUtil.java
index af8d77cc39..ac28a3ea2f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductUpdateUtil.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductUpdateUtil.java
@@ -410,11 +410,11 @@ public class LoanProductUpdateUtil {
         }
 
         if 
(command.isChangeInIntegerParameterNamedWithNullCheck(LoanProductConstants.installmentAmountInMultiplesOfParamName,
-                loanProduct.getInstallmentAmountInMultiplesOf())) {
+                
loanProduct.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf())) 
{
             final Integer newValue = 
command.integerValueOfParameterNamed(LoanProductConstants.installmentAmountInMultiplesOfParamName);
             
actualChanges.put(LoanProductConstants.installmentAmountInMultiplesOfParamName, 
newValue);
             actualChanges.put("locale", localeAsInput);
-            loanProduct.setInstallmentAmountInMultiplesOf(newValue);
+            
loanProduct.getLoanProductRelatedDetail().setInstallmentAmountInMultiplesOf(newValue);
         }
 
         if 
(command.isChangeInBooleanParameterNamed(LoanProductConstants.CAN_USE_FOR_TOPUP,
 loanProduct.isCanUseForTopup())) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index c99e1cf08e..bbd9150810 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -444,9 +444,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
                             .getDisbursedAmountPercentageForDownPayment();
                     Money downPaymentMoney = Money.of(loan.getCurrency(),
                             
MathUtil.percentageOf(amountToDisburse.getAmount(), 
disbursedAmountPercentageForDownPayment, 19));
-                    if 
(loan.getLoanProduct().getInstallmentAmountInMultiplesOf() != null) {
+                    if 
(loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf() != 
null) {
                         downPaymentMoney = 
Money.roundToMultiplesOf(downPaymentMoney,
-                                
loan.getLoanProduct().getInstallmentAmountInMultiplesOf());
+                                
loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf());
                     }
                     final AccountTransferDTO accountTransferDTO = new 
AccountTransferDTO(actualDisbursementDate,
                             downPaymentMoney.getAmount(), 
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
index 56c2ef8c96..181410b91c 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
@@ -254,7 +254,7 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
                     + "lp.allow_multiple_disbursals as multiDisburseLoan, 
lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as 
outstandingLoanBalance, "
                     + "lp.disallow_expected_disbursements as 
disallowExpectedDisbursements, lp.allow_approved_disbursed_amounts_over_applied 
as allowApprovedDisbursedAmountsOverApplied, lp.over_applied_calculation_type 
as overAppliedCalculationType, over_applied_number as overAppliedNumber, "
                     + "lp.days_in_month_enum as daysInMonth, 
lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as 
isInterestRecalculationEnabled, "
-                    + "lp.can_define_fixed_emi_amount as 
canDefineInstallmentAmount, lp.instalment_amount_in_multiples_of as 
installmentAmountInMultiplesOf, "
+                    + "lp.can_define_fixed_emi_amount as 
canDefineInstallmentAmount, lp.installment_amount_in_multiples_of as 
installmentAmountInMultiplesOf, "
                     + "lp.due_days_for_repayment_event as 
dueDaysForRepaymentEvent, lp.overdue_days_for_repayment_event as 
overDueDaysForRepaymentEvent, lp.enable_down_payment as enableDownPayment, 
lp.disbursed_amount_percentage_for_down_payment as 
disbursedAmountPercentageForDownPayment, 
lp.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment, 
lp.repayment_start_date_type_enum as repaymentStartDateType, "
                     + "lp.enable_installment_level_delinquency as 
enableInstallmentLevelDelinquency, "
                     + "lpr.pre_close_interest_calculation_strategy as 
preCloseInterestCalculationStrategy, "
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java
index 7323ea61bd..231e1bad39 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java
@@ -160,6 +160,7 @@ public class LoanDownPaymentHandlerServiceImplTest {
         
when(downPaymentMoney.getCurrencyCode()).thenReturn(loanCurrency.getCode());
         
when(overPaymentPortionMoney.getCurrencyCode()).thenReturn(loanCurrency.getCode());
 
+        
when(loanRepaymentRelatedDetail.getInstallmentAmountInMultiplesOf()).thenReturn(10);
         
when(loanForProcessing.getLoanRepaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail);
         
when(loanForProcessing.repaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail);
         
when(loanRepaymentRelatedDetail.isInterestRecalculationEnabled()).thenReturn(true);
@@ -168,7 +169,6 @@ public class LoanDownPaymentHandlerServiceImplTest {
         when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
         when(loanForProcessing.loanCurrency()).thenReturn(loanCurrency);
         when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
-        when(loanProduct.getInstallmentAmountInMultiplesOf()).thenReturn(10);
         
when(loanForProcessing.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
         when(loanTransactionProcessingService.reprocessLoanTransactions(any(), 
any(), any(), any(), any(), any()))
                 .thenReturn(changedTransactionDetail);


Reply via email to