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 24cb62443 [FINERACT-1968] charge payment adv.paym.alloc.strategy
24cb62443 is described below

commit 24cb62443ed6b8691b46d6991aa401abbe2b10eb
Author: taskain7 <[email protected]>
AuthorDate: Thu Oct 26 17:18:28 2023 +0200

    [FINERACT-1968] charge payment adv.paym.alloc.strategy
---
 .../loanaccount/domain/LoanTransaction.java        |   9 +-
 .../domain/PaymentAllocationTransactionType.java   |   3 +-
 ...dvancedPaymentScheduleTransactionProcessor.java | 275 ++++++++++------
 ...gePaymentWithAdvancedPaymentAllocationTest.java | 360 +++++++++++++++++++++
 4 files changed, 546 insertions(+), 101 deletions(-)

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 07a5b2554..bca005632 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
@@ -275,10 +275,15 @@ public class LoanTransaction extends 
AbstractAuditableWithUTCDateTimeCustom {
     }
 
     public static LoanTransaction copyTransactionProperties(final 
LoanTransaction loanTransaction) {
-        return new LoanTransaction(loanTransaction.loan, 
loanTransaction.office, loanTransaction.typeOf, loanTransaction.dateOf,
-                loanTransaction.amount, loanTransaction.principalPortion, 
loanTransaction.interestPortion,
+        LoanTransaction newTransaction = new 
LoanTransaction(loanTransaction.loan, loanTransaction.office, 
loanTransaction.typeOf,
+                loanTransaction.dateOf, loanTransaction.amount, 
loanTransaction.principalPortion, loanTransaction.interestPortion,
                 loanTransaction.feeChargesPortion, 
loanTransaction.penaltyChargesPortion, loanTransaction.overPaymentPortion,
                 loanTransaction.reversed, loanTransaction.paymentDetail, 
loanTransaction.externalId);
+
+        if 
(LoanTransactionType.CHARGE_PAYMENT.equals(loanTransaction.getTypeOf())) {
+            
newTransaction.getLoanChargesPaid().addAll(loanTransaction.getLoanChargesPaid());
+        }
+        return newTransaction;
     }
 
     public static LoanTransaction accrueLoanCharge(final Loan loan, final 
Office office, final Money amount, final LocalDate applyDate,
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/PaymentAllocationTransactionType.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/PaymentAllocationTransactionType.java
index adf967f6c..85b3d865d 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/PaymentAllocationTransactionType.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/PaymentAllocationTransactionType.java
@@ -37,7 +37,8 @@ public enum PaymentAllocationTransactionType {
     GOODWILL_CREDIT(LoanTransactionType.GOODWILL_CREDIT, "Goodwill credit"), //
     CHARGE_REFUND(LoanTransactionType.CHARGE_REFUND, "Charge refund"), //
     CHARGE_ADJUSTMENT(LoanTransactionType.CHARGE_ADJUSTMENT, "Charge 
adjustment"), //
-    WAIVE_INTEREST(LoanTransactionType.WAIVE_INTEREST, "Waive interest");//
+    WAIVE_INTEREST(LoanTransactionType.WAIVE_INTEREST, "Waive interest"), 
CHARGE_PAYMENT(LoanTransactionType.CHARGE_PAYMENT,
+            "Charge payment");//
 
     private final LoanTransactionType loanTransactionType;
     private final String humanReadableName;
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index 57fc21f19..7ab5a8a91 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -26,6 +26,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.Setter;
@@ -38,6 +39,7 @@ import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
 import 
org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -183,11 +185,11 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             case WRITEOFF -> handleWriteOff(loanTransaction, currency, 
installments);
             case REFUND_FOR_ACTIVE_LOAN -> handleRefund(loanTransaction, 
currency, installments, charges);
             case CHARGEBACK -> handleChargeback(loanTransaction, currency, 
overpaidAmount, installments);
-
             case REPAYMENT, MERCHANT_ISSUED_REFUND, PAYOUT_REFUND, 
GOODWILL_CREDIT, CHARGE_REFUND, CHARGE_ADJUSTMENT, DOWN_PAYMENT,
                     WAIVE_INTEREST, RECOVERY_REPAYMENT ->
                 handleRepayment(loanTransaction, currency, installments, 
charges);
             case CHARGE_OFF -> handleChargeOff(loanTransaction, currency, 
installments);
+            case CHARGE_PAYMENT -> handleChargePayment(loanTransaction, 
currency, installments, charges);
             // TODO: Cover rest of the transaction types
             default -> {
                 log.warn("Unhandled transaction processing for transaction 
type: {}", loanTransaction.getTypeOf());
@@ -249,6 +251,153 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             loanTransaction.resetDerivedComponents();
         }
         Money transactionAmountUnprocessed = 
loanTransaction.getAmount(currency);
+        processTransaction(loanTransaction, currency, installments, 
transactionAmountUnprocessed, charges);
+    }
+
+    private LoanTransactionToRepaymentScheduleMapping getTransactionMapping(
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, LoanTransaction loanTransaction,
+            LoanRepaymentScheduleInstallment currentInstallment, 
MonetaryCurrency currency) {
+        Money zero = Money.zero(currency);
+        LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping = transactionMappings.stream()
+                .filter(e -> loanTransaction.equals(e.getLoanTransaction()))
+                .filter(e -> 
currentInstallment.equals(e.getLoanRepaymentScheduleInstallment())).findFirst().orElse(null);
+        if (loanTransactionToRepaymentScheduleMapping == null) {
+            loanTransactionToRepaymentScheduleMapping = 
LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
+                    currentInstallment, zero, zero, zero, zero);
+            transactionMappings.add(loanTransactionToRepaymentScheduleMapping);
+        }
+        return loanTransactionToRepaymentScheduleMapping;
+    }
+
+    private Money payAllocation(PaymentAllocationType paymentAllocationType, 
LoanRepaymentScheduleInstallment currentInstallment,
+            LoanTransaction loanTransaction, Money 
transactionAmountUnprocessed,
+            LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping, Balances balances) {
+        LocalDate transactionDate = loanTransaction.getTransactionDate();
+        Money zero = transactionAmountUnprocessed.zero();
+        return switch (paymentAllocationType.getAllocationType()) {
+            case PENALTY -> {
+                Money subPenaltyPortion = 
currentInstallment.payPenaltyChargesComponent(transactionDate, 
transactionAmountUnprocessed);
+                
balances.setAggregatedPenaltyChargesPortion(balances.getAggregatedPenaltyChargesPortion().add(subPenaltyPortion));
+                
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero, 
zero, subPenaltyPortion);
+                yield subPenaltyPortion;
+            }
+            case FEE -> {
+                Money subFeePortion = 
currentInstallment.payFeeChargesComponent(transactionDate, 
transactionAmountUnprocessed);
+                
balances.setAggregatedFeeChargesPortion(balances.getAggregatedFeeChargesPortion().add(subFeePortion));
+                
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero, 
subFeePortion, zero);
+                yield subFeePortion;
+            }
+            case INTEREST -> {
+                Money subInterestPortion = 
currentInstallment.payInterestComponent(transactionDate, 
transactionAmountUnprocessed);
+                
balances.setAggregatedInterestPortion(balances.getAggregatedInterestPortion().add(subInterestPortion));
+                
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, 
subInterestPortion, zero, zero);
+                yield subInterestPortion;
+            }
+            case PRINCIPAL -> {
+                Money subPrincipalPortion = 
currentInstallment.payPrincipalComponent(transactionDate, 
transactionAmountUnprocessed);
+                
balances.setAggregatedPrincipalPortion(balances.getAggregatedPrincipalPortion().add(subPrincipalPortion));
+                
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, 
subPrincipalPortion, zero, zero, zero);
+                yield subPrincipalPortion;
+            }
+        };
+    }
+
+    private void 
addToTransactionMapping(LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping,
+            Money principalPortion, Money interestPortion, Money feePortion, 
Money penaltyPortion) {
+        BigDecimal aggregatedPenalty = ObjectUtils
+                
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getPenaltyChargesPortion(),
 BigDecimal.ZERO)
+                .add(penaltyPortion.getAmount());
+        BigDecimal aggregatedFee = ObjectUtils
+                
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getFeeChargesPortion(),
 BigDecimal.ZERO)
+                .add(feePortion.getAmount());
+        BigDecimal aggregatedInterest = ObjectUtils
+                
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getInterestPortion(), 
BigDecimal.ZERO)
+                .add(interestPortion.getAmount());
+        BigDecimal aggregatedPrincipal = ObjectUtils
+                
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getPrincipalPortion(), 
BigDecimal.ZERO)
+                .add(principalPortion.getAmount());
+        
loanTransactionToRepaymentScheduleMapping.setComponents(aggregatedPrincipal, 
aggregatedInterest, aggregatedFee, aggregatedPenalty);
+    }
+
+    private void handleOverpayment(Money overpaymentPortion, LoanTransaction 
loanTransaction) {
+        if (overpaymentPortion.isGreaterThanZero()) {
+            onLoanOverpayment(loanTransaction, overpaymentPortion);
+            loanTransaction.updateOverPayments(overpaymentPortion);
+        }
+    }
+
+    private void handleChargeOff(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
+            List<LoanRepaymentScheduleInstallment> installments) {
+        loanTransaction.resetDerivedComponents();
+        // determine how much is outstanding total and breakdown for 
principal, interest and charges
+        Money principalPortion = Money.zero(currency);
+        Money interestPortion = Money.zero(currency);
+        Money feeChargesPortion = Money.zero(currency);
+        Money penaltychargesPortion = Money.zero(currency);
+        for (final LoanRepaymentScheduleInstallment currentInstallment : 
installments) {
+            if (currentInstallment.isNotFullyPaidOff()) {
+                principalPortion = 
principalPortion.plus(currentInstallment.getPrincipalOutstanding(currency));
+                interestPortion = 
interestPortion.plus(currentInstallment.getInterestOutstanding(currency));
+                feeChargesPortion = 
feeChargesPortion.plus(currentInstallment.getFeeChargesOutstanding(currency));
+                penaltychargesPortion = 
penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
+            }
+        }
+
+        loanTransaction.updateComponentsAndTotal(principalPortion, 
interestPortion, feeChargesPortion, penaltychargesPortion);
+    }
+
+    private void handleChargePayment(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
+            List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> charges) {
+        Money zero = Money.zero(currency);
+        Money feeChargesPortion = zero;
+        Money penaltyChargesPortion = zero;
+        List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = 
new ArrayList<>();
+        LoanChargePaidBy loanChargePaidBy = 
loanTransaction.getLoanChargesPaid().stream().findFirst().get();
+        LoanCharge loanCharge = loanChargePaidBy.getLoanCharge();
+        Money amountToBePaid = Money.of(currency, loanTransaction.getAmount());
+        if 
(loanCharge.getAmountOutstanding(currency).isLessThan(amountToBePaid)) {
+            amountToBePaid = loanCharge.getAmountOutstanding(currency);
+        }
+
+        LocalDate startDate = loanTransaction.getLoan().getDisbursementDate();
+
+        Money unprocessed = loanTransaction.getAmount(currency);
+        for (final LoanRepaymentScheduleInstallment installment : 
installments) {
+            boolean isDue = installment.isFirstPeriod()
+                    ? 
loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(startDate, 
installment.getDueDate())
+                    : 
loanCharge.isDueForCollectionFromAndUpToAndIncluding(startDate, 
installment.getDueDate());
+            if (isDue) {
+                Integer installmentNumber = installment.getInstallmentNumber();
+                Money paidAmount = 
loanCharge.updatePaidAmountBy(amountToBePaid, installmentNumber, zero);
+
+                LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
+                        transactionMappings, loanTransaction, installment, 
currency);
+
+                if (loanTransaction.isPenaltyPayment()) {
+                    penaltyChargesPortion = 
installment.payPenaltyChargesComponent(loanTransaction.getTransactionDate(), 
paidAmount);
+                    loanTransaction.setLoanChargesPaid(Collections
+                            .singleton(new LoanChargePaidBy(loanTransaction, 
loanCharge, paidAmount.getAmount(), installmentNumber)));
+                    
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero, 
zero, penaltyChargesPortion);
+                } else {
+                    feeChargesPortion = 
installment.payFeeChargesComponent(loanTransaction.getTransactionDate(), 
paidAmount);
+                    loanTransaction.setLoanChargesPaid(Collections
+                            .singleton(new LoanChargePaidBy(loanTransaction, 
loanCharge, paidAmount.getAmount(), installmentNumber)));
+                    
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero, 
feeChargesPortion, zero);
+                }
+
+                loanTransaction.updateComponents(zero, zero, 
feeChargesPortion, penaltyChargesPortion);
+                unprocessed = 
loanTransaction.getAmount(currency).minus(paidAmount);
+                
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
+            }
+        }
+
+        if (unprocessed.isGreaterThanZero()) {
+            processTransaction(loanTransaction, currency, installments, 
unprocessed, charges);
+        }
+    }
+
+    private void processTransaction(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
+            List<LoanRepaymentScheduleInstallment> installments, Money 
transactionAmountUnprocessed, Set<LoanCharge> charges) {
         Money zero = Money.zero(currency);
         List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = 
new ArrayList<>();
 
@@ -345,116 +494,46 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                 balances.getAggregatedFeeChargesPortion(), 
balances.getAggregatedPenaltyChargesPortion());
         
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
 
+        payAdditionalCharges(loanTransaction, currency, charges);
+
+        handleOverpayment(transactionAmountUnprocessed, loanTransaction);
+    }
+
+    private void payAdditionalCharges(LoanTransaction loanTransaction, 
MonetaryCurrency currency, Set<LoanCharge> charges) {
+        final Set<LoanCharge> paidFeeCharges = 
loanTransaction.getLoanChargesPaid().stream() //
+                .map(LoanChargePaidBy::getLoanCharge) //
+                .filter(LoanCharge::isFeeCharge).collect(Collectors.toSet());
+        final Set<LoanCharge> paidPenaltyCharges = 
loanTransaction.getLoanChargesPaid().stream() //
+                .map(LoanChargePaidBy::getLoanCharge) //
+                
.filter(LoanCharge::isPenaltyCharge).collect(Collectors.toSet());
         // TODO: rewrite to provide sorted list
         final Set<LoanCharge> loanFees = extractFeeCharges(charges);
         final Set<LoanCharge> loanPenalties = extractPenaltyCharges(charges);
+        loanFees.removeAll(paidFeeCharges);
+        loanPenalties.removeAll(paidPenaltyCharges);
+
+        BigDecimal sumFeePaidAmount = paidFeeCharges.stream() //
+                .map(paidCharge -> paidCharge.getAmountPaid(currency)) //
+                .map(Money::getAmount) //
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        BigDecimal sumPenaltyPaidAmount = paidPenaltyCharges.stream() //
+                .map(paidCharge -> paidCharge.getAmountPaid(currency)) //
+                .map(Money::getAmount) //
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
 
         if (loanTransaction.isNotWaiver() && !loanTransaction.isAccrual()) {
-            Money feeCharges = loanTransaction.getFeeChargesPortion(currency);
-            Money penaltyCharges = 
loanTransaction.getPenaltyChargesPortion(currency);
+            Money feeCharges = 
loanTransaction.getFeeChargesPortion(currency).minus(sumFeePaidAmount);
+            Money penaltyCharges = 
loanTransaction.getPenaltyChargesPortion(currency).minus(sumPenaltyPaidAmount);
+
             if (feeCharges.isGreaterThanZero()) {
-                // TODO: Rewrite to exclude charge payment
                 updateChargesPaidAmountBy(loanTransaction, feeCharges, 
loanFees, null);
             }
 
             if (penaltyCharges.isGreaterThanZero()) {
-                // TODO: Rewrite to exclude charge payment
                 updateChargesPaidAmountBy(loanTransaction, penaltyCharges, 
loanPenalties, null);
             }
         }
-        handleOverpayment(transactionAmountUnprocessed, loanTransaction);
-    }
-
-    private LoanTransactionToRepaymentScheduleMapping getTransactionMapping(
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, LoanTransaction loanTransaction,
-            LoanRepaymentScheduleInstallment currentInstallment, 
MonetaryCurrency currency) {
-        Money zero = Money.zero(currency);
-        LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping = transactionMappings.stream()
-                .filter(e -> loanTransaction.equals(e.getLoanTransaction()))
-                .filter(e -> 
currentInstallment.equals(e.getLoanRepaymentScheduleInstallment())).findFirst().orElse(null);
-        if (loanTransactionToRepaymentScheduleMapping == null) {
-            loanTransactionToRepaymentScheduleMapping = 
LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
-                    currentInstallment, zero, zero, zero, zero);
-            transactionMappings.add(loanTransactionToRepaymentScheduleMapping);
-        }
-        return loanTransactionToRepaymentScheduleMapping;
-    }
-
-    private Money payAllocation(PaymentAllocationType paymentAllocationType, 
LoanRepaymentScheduleInstallment currentInstallment,
-            LoanTransaction loanTransaction, Money 
transactionAmountUnprocessed,
-            LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping, Balances balances) {
-        LocalDate transactionDate = loanTransaction.getTransactionDate();
-        Money zero = transactionAmountUnprocessed.zero();
-        return switch (paymentAllocationType.getAllocationType()) {
-            case PENALTY -> {
-                Money subPenaltyPortion = 
currentInstallment.payPenaltyChargesComponent(transactionDate, 
transactionAmountUnprocessed);
-                
balances.setAggregatedPenaltyChargesPortion(balances.getAggregatedPenaltyChargesPortion().add(subPenaltyPortion));
-                
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero, 
zero, subPenaltyPortion);
-                yield subPenaltyPortion;
-            }
-            case FEE -> {
-                Money subFeePortion = 
currentInstallment.payFeeChargesComponent(transactionDate, 
transactionAmountUnprocessed);
-                
balances.setAggregatedFeeChargesPortion(balances.getAggregatedFeeChargesPortion().add(subFeePortion));
-                
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero, 
subFeePortion, zero);
-                yield subFeePortion;
-            }
-            case INTEREST -> {
-                Money subInterestPortion = 
currentInstallment.payInterestComponent(transactionDate, 
transactionAmountUnprocessed);
-                
balances.setAggregatedInterestPortion(balances.getAggregatedInterestPortion().add(subInterestPortion));
-                
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, 
subInterestPortion, zero, zero);
-                yield subInterestPortion;
-            }
-            case PRINCIPAL -> {
-                Money subPrincipalPortion = 
currentInstallment.payPrincipalComponent(transactionDate, 
transactionAmountUnprocessed);
-                
balances.setAggregatedPrincipalPortion(balances.getAggregatedPrincipalPortion().add(subPrincipalPortion));
-                
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, 
subPrincipalPortion, zero, zero, zero);
-                yield subPrincipalPortion;
-            }
-        };
-    }
-
-    private void 
addToTransactionMapping(LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping,
-            Money principalPortion, Money interestPortion, Money feePortion, 
Money penaltyPortion) {
-        BigDecimal aggregatedPenalty = ObjectUtils
-                
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getPenaltyChargesPortion(),
 BigDecimal.ZERO)
-                .add(penaltyPortion.getAmount());
-        BigDecimal aggregatedFee = ObjectUtils
-                
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getFeeChargesPortion(),
 BigDecimal.ZERO)
-                .add(feePortion.getAmount());
-        BigDecimal aggregatedInterest = ObjectUtils
-                
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getInterestPortion(), 
BigDecimal.ZERO)
-                .add(interestPortion.getAmount());
-        BigDecimal aggregatedPrincipal = ObjectUtils
-                
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getPrincipalPortion(), 
BigDecimal.ZERO)
-                .add(principalPortion.getAmount());
-        
loanTransactionToRepaymentScheduleMapping.setComponents(aggregatedPrincipal, 
aggregatedInterest, aggregatedFee, aggregatedPenalty);
-    }
-
-    private void handleOverpayment(Money overpaymentPortion, LoanTransaction 
loanTransaction) {
-        if (overpaymentPortion.isGreaterThanZero()) {
-            onLoanOverpayment(loanTransaction, overpaymentPortion);
-            loanTransaction.updateOverPayments(overpaymentPortion);
-        }
-    }
-
-    private void handleChargeOff(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
-            List<LoanRepaymentScheduleInstallment> installments) {
-        loanTransaction.resetDerivedComponents();
-        // determine how much is outstanding total and breakdown for 
principal, interest and charges
-        Money principalPortion = Money.zero(currency);
-        Money interestPortion = Money.zero(currency);
-        Money feeChargesPortion = Money.zero(currency);
-        Money penaltychargesPortion = Money.zero(currency);
-        for (final LoanRepaymentScheduleInstallment currentInstallment : 
installments) {
-            if (currentInstallment.isNotFullyPaidOff()) {
-                principalPortion = 
principalPortion.plus(currentInstallment.getPrincipalOutstanding(currency));
-                interestPortion = 
interestPortion.plus(currentInstallment.getInterestOutstanding(currency));
-                feeChargesPortion = 
feeChargesPortion.plus(currentInstallment.getFeeChargesOutstanding(currency));
-                penaltychargesPortion = 
penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
-            }
-        }
-
-        loanTransaction.updateComponentsAndTotal(principalPortion, 
interestPortion, feeChargesPortion, penaltychargesPortion);
     }
 
     @AllArgsConstructor
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargePaymentWithAdvancedPaymentAllocationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargePaymentWithAdvancedPaymentAllocationTest.java
new file mode 100644
index 000000000..33601c650
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargePaymentWithAdvancedPaymentAllocationTest.java
@@ -0,0 +1,360 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static 
org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity.LIABILITY_TRANSFER;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.AdvancedPaymentData;
+import org.apache.fineract.client.models.BusinessDateRequest;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PaymentAllocationOrder;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdRequest;
+import org.apache.fineract.client.models.PostLoansRequest;
+import org.apache.fineract.client.models.PostLoansResponse;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.accounting.Account;
+import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
+import 
org.apache.fineract.integrationtests.common.accounting.FinancialActivityAccountHelper;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker;
+import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
+import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(LoanTestLifecycleExtension.class)
+@Slf4j
+public class LoanChargePaymentWithAdvancedPaymentAllocationTest {
+
+    private static final String DATETIME_PATTERN = "dd MMMM yyyy";
+    private static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
+    private static final DateTimeFormatter DATE_FORMATTER = new 
DateTimeFormatterBuilder().appendPattern(DATETIME_PATTERN).toFormatter();
+    private static RequestSpecification requestSpec;
+    private static ResponseSpecification responseSpec;
+    private static LoanTransactionHelper loanTransactionHelper;
+    private static AccountHelper accountHelper;
+    private static Integer commonLoanProductId;
+    private static PostClientsResponse client;
+    private static BusinessDateHelper businessDateHelper;
+    private static SchedulerJobHelper scheduleJobHelper;
+    private static SavingsAccountHelper savingsAccountHelper;
+    private static SavingsProductHelper savingsProductHelper;
+    private static FinancialActivityAccountHelper 
financialActivityAccountHelper;
+    private static Integer financialActivityAccountId;
+    private static Account liabilityTransferAccount;
+
+    @BeforeAll
+    public static void setup() {
+        Utils.initializeRESTAssured();
+        ClientHelper clientHelper = new ClientHelper(requestSpec, 
responseSpec);
+        requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        requestSpec.header("Fineract-Platform-TenantId", "default");
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+
+        loanTransactionHelper = new LoanTransactionHelper(requestSpec, 
responseSpec);
+        accountHelper = new AccountHelper(requestSpec, responseSpec);
+        final Account assetAccount = accountHelper.createAssetAccount();
+        final Account incomeAccount = accountHelper.createIncomeAccount();
+        final Account expenseAccount = accountHelper.createExpenseAccount();
+        final Account overpaymentAccount = 
accountHelper.createLiabilityAccount();
+        client = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
+        businessDateHelper = new BusinessDateHelper();
+        scheduleJobHelper = new SchedulerJobHelper(requestSpec);
+        savingsAccountHelper = new SavingsAccountHelper(requestSpec, 
responseSpec);
+        savingsProductHelper = new SavingsProductHelper();
+        commonLoanProductId = createLoanProduct("500", "15", "4", 
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
+        financialActivityAccountHelper = new 
FinancialActivityAccountHelper(requestSpec);
+
+        List<HashMap> financialActivities = 
financialActivityAccountHelper.getAllFinancialActivityAccounts(responseSpec);
+        if (financialActivities.isEmpty()) {
+            /** Setup liability transfer account **/
+            /** Create a Liability and an Asset Transfer Account **/
+            liabilityTransferAccount = accountHelper.createLiabilityAccount();
+            Assertions.assertNotNull(liabilityTransferAccount);
+
+            /*** Create A Financial Activity to Account Mapping **/
+            financialActivityAccountId = (Integer) 
financialActivityAccountHelper.createFinancialActivityAccount(
+                    LIABILITY_TRANSFER.getValue(), 
liabilityTransferAccount.getAccountID(), responseSpec,
+                    CommonConstants.RESPONSE_RESOURCE_ID);
+            Assertions.assertNotNull(financialActivityAccountId);
+        } else {
+            for (HashMap financialActivity : financialActivities) {
+                HashMap financialActivityData = (HashMap) 
financialActivity.get("financialActivityData");
+                if 
(financialActivityData.get("id").equals(FinancialActivityAccountsTest.LIABILITY_TRANSFER_FINANCIAL_ACTIVITY_ID))
 {
+                    HashMap glAccountData = (HashMap) 
financialActivity.get("glAccountData");
+                    liabilityTransferAccount = new Account((Integer) 
glAccountData.get("id"), Account.AccountType.LIABILITY);
+                    financialActivityAccountId = (Integer) 
financialActivity.get("id");
+                    break;
+                }
+            }
+        }
+    }
+
+    @AfterAll
+    public static void tearDown() {
+        Integer deletedFinancialActivityAccountId = 
financialActivityAccountHelper
+                .deleteFinancialActivityAccount(financialActivityAccountId, 
responseSpec, CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(deletedFinancialActivityAccountId);
+        Assertions.assertEquals(financialActivityAccountId, 
deletedFinancialActivityAccountId);
+    }
+
+    @Test
+    public void feeAndPenaltyChargePaymentWithDefaultAllocationRuleTest() {
+        try {
+
+            final String jobName = "Transfer Fee For Loans From Savings";
+            final String startDate = "10 April 2022";
+
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
+                    .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+
+            final Integer savingsId = 
createSavingsAccountDailyPosting(client.getClientId().intValue(), startDate);
+
+            savingsAccountHelper.depositToSavingsAccount(savingsId, "10000", 
startDate, CommonConstants.RESPONSE_RESOURCE_ID);
+
+            final PostLoansResponse loanResponse = 
applyForLoanApplication(client.getClientId(), commonLoanProductId, 1000L, 45, 
15, 3, 0,
+                    "01 January 2023", "01 January 2023");
+
+            int loanId = loanResponse.getLoanId().intValue();
+
+            loanTransactionHelper.updateLoan(loanId,
+                    updateLoanJson(client.getClientId().intValue(), 
commonLoanProductId, savingsId.toString()));
+
+            loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
+                    new 
PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
+                            .approvedOnDate("01 January 2023").locale("en"));
+
+            loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+                    new PostLoansLoanIdRequest().actualDisbursementDate("01 
January 2023").dateFormat(DATETIME_PATTERN)
+                            
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en"));
+
+            final float feePortion = 50.0f;
+            final float penaltyPortion = 100.0f;
+
+            Integer fee = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateWithAccountTransferJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
+                            String.valueOf(feePortion), false));
+
+            Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateWithAccountTransferJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
+                            String.valueOf(penaltyPortion), true));
+
+            LocalDate targetDate = LocalDate.of(2023, 1, 3);
+            final String penaltyChargeAddedDate = 
DATE_FORMATTER.format(targetDate);
+            loanTransactionHelper.addChargesForLoan(loanId, 
LoanTransactionHelper
+                    
.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), 
penaltyChargeAddedDate, String.valueOf(feePortion)));
+
+            loanTransactionHelper.addChargesForLoan(loanId, 
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(
+                    String.valueOf(penalty), penaltyChargeAddedDate, 
String.valueOf(penaltyPortion)));
+
+            loanTransactionHelper.noAccrualTransactionForRepayment(loanId);
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails((long) loanId);
+
+            assertEquals(5, 
loanDetails.getRepaymentSchedule().getPeriods().size());
+            assertEquals(feePortion, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesDue());
+            assertEquals(feePortion, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesOutstanding());
+            assertEquals(penaltyPortion, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesDue());
+            assertEquals(penaltyPortion, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesOutstanding());
+            assertEquals(400.0f, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalDueForPeriod());
+            assertEquals(400.0f, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalOutstandingForPeriod());
+            assertEquals(LocalDate.of(2023, 1, 16), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+
+            scheduleJobHelper.executeAndAwaitJob(jobName);
+
+            loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+            assertEquals(5, 
loanDetails.getRepaymentSchedule().getPeriods().size());
+            assertEquals(feePortion, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesDue());
+            assertEquals(0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesOutstanding());
+            assertEquals(penaltyPortion, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesDue());
+            assertEquals(0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesOutstanding());
+            assertEquals(400, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalDueForPeriod());
+            assertEquals(250, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalOutstandingForPeriod());
+            assertEquals(LocalDate.of(2023, 1, 16), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+    }
+
+    private Integer createSavingsAccountDailyPosting(final Integer clientID, 
String startDate) {
+        final Integer savingsProductID = createSavingsProductDailyPosting();
+        Assertions.assertNotNull(savingsProductID);
+        final Integer savingsId = 
savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, 
savingsProductID, ACCOUNT_TYPE_INDIVIDUAL,
+                startDate);
+        Assertions.assertNotNull(savingsId);
+        HashMap savingsStatusHashMap = 
savingsAccountHelper.approveSavingsOnDate(savingsId, startDate);
+        SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+        savingsStatusHashMap = 
savingsAccountHelper.activateSavingsAccount(savingsId, startDate);
+        SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+        return savingsId;
+    }
+
+    private Integer createSavingsProductDailyPosting() {
+        final String savingsProductJSON = 
savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
+                
.withInterestPostingPeriodTypeAsMonthly().withInterestCalculationPeriodTypeAsDailyBalance()
+                .withMinimumOpenningBalance("10000.0").build();
+        return SavingsProductHelper.createSavingsProduct(savingsProductJSON, 
requestSpec, responseSpec);
+    }
+
+    private static Integer createLoanProduct(final String principal, final 
String repaymentAfterEvery, final String numberOfRepayments,
+            final Account... accounts) {
+        AdvancedPaymentData defaultAllocation = 
createDefaultPaymentAllocation();
+        AdvancedPaymentData goodwillCreditAllocation = 
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT");
+        AdvancedPaymentData merchantIssuedRefundAllocation = 
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION");
+        AdvancedPaymentData payoutRefundAllocation = 
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT");
+        log.info("------------------------------CREATING NEW LOAN PRODUCT 
---------------------------------------");
+        final String loanProductJSON = new 
LoanProductTestBuilder().withMinPrincipal(principal).withPrincipal(principal)
+                
.withRepaymentTypeAsDays().withRepaymentAfterEvery(repaymentAfterEvery).withNumberOfRepayments(numberOfRepayments)
+                .withEnableDownPayment(true, "25", 
true).withinterestRatePerPeriod("0").withInterestRateFrequencyTypeAsMonths()
+                
.withRepaymentStrategy(AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)
+                
.withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRulePeriodicAccrual(accounts)
+                .addAdvancedPaymentAllocation(defaultAllocation, 
goodwillCreditAllocation, merchantIssuedRefundAllocation,
+                        payoutRefundAllocation)
+                
.withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", 
"0").build(null);
+        return loanTransactionHelper.getLoanProductId(loanProductJSON);
+    }
+
+    private static PostLoansResponse applyForLoanApplication(final Long 
clientId, final Integer loanProductId, final Long principal,
+            final int loanTermFrequency, final int repaymentAfterEvery, final 
int numberOfRepayments, final int interestRate,
+            final String expectedDisbursementDate, final String 
submittedOnDate) {
+        log.info("--------------------------------APPLYING FOR LOAN 
APPLICATION--------------------------------");
+        return loanTransactionHelper.applyLoan(new 
PostLoansRequest().clientId(clientId).productId(loanProductId.longValue())
+                
.expectedDisbursementDate(expectedDisbursementDate).dateFormat(DATETIME_PATTERN)
+                
.transactionProcessingStrategyCode(AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)
+                
.locale("en").submittedOnDate(submittedOnDate).amortizationType(1).interestRatePerPeriod(interestRate)
+                
.interestCalculationPeriodType(1).interestType(0).repaymentFrequencyType(0).repaymentEvery(repaymentAfterEvery)
+                
.repaymentFrequencyType(0).numberOfRepayments(numberOfRepayments).loanTermFrequency(loanTermFrequency)
+                
.loanTermFrequencyType(0).principal(BigDecimal.valueOf(principal)).loanType("individual"));
+    }
+
+    private String updateLoanJson(final Integer clientID, final Integer 
loanProductID, String savingsId) {
+        log.info("--------------------------------APPLYING FOR LOAN 
APPLICATION--------------------------------");
+        List<HashMap> collaterals = new ArrayList<>();
+        final Integer collateralId = 
CollateralManagementHelper.createCollateralProduct(this.requestSpec, 
this.responseSpec);
+        Assertions.assertNotNull(collateralId);
+        final Integer clientCollateralId = 
CollateralManagementHelper.createClientCollateral(this.requestSpec, 
this.responseSpec,
+                clientID.toString(), collateralId);
+        Assertions.assertNotNull(clientCollateralId);
+        addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
+
+        final String loanApplicationJSON = new LoanApplicationTestBuilder() //
+                .withPrincipal("1,000.00") //
+                .withLoanTermFrequency("45") //
+                .withLoanTermFrequencyAsDays() //
+                .withNumberOfRepayments("3") //
+                .withRepaymentEveryAfter("15") //
+                .withRepaymentFrequencyTypeAsDays() //
+                .withInterestRatePerPeriod("0") //
+                
.withRepaymentStrategy(AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)
+                .withAmortizationTypeAsEqualInstallments() //
+                .withInterestTypeAsDecliningBalance() //
+                .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
+                .withExpectedDisbursementDate("01 January 2023") //
+                .withSubmittedOnDate("01 January 2023") //
+                .withCollaterals(collaterals) //
+                .build(clientID.toString(), loanProductID.toString(), 
savingsId);
+        return loanApplicationJSON;
+    }
+
+    private void addCollaterals(List<HashMap> collaterals, Integer 
collateralId, BigDecimal quantity) {
+        collaterals.add(collaterals(collateralId, quantity));
+    }
+
+    private HashMap<String, String> collaterals(Integer collateralId, 
BigDecimal quantity) {
+        HashMap<String, String> collateral = new HashMap<>(2);
+        collateral.put("clientCollateralId", collateralId.toString());
+        collateral.put("quantity", quantity.toString());
+        return collateral;
+    }
+
+    private static AdvancedPaymentData createPaymentAllocation(String 
transactionType, String futureInstallmentAllocationRule) {
+        AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
+        advancedPaymentData.setTransactionType(transactionType);
+        
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
+
+        List<PaymentAllocationOrder> paymentAllocationOrders = 
getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
+                PaymentAllocationType.PAST_DUE_FEE, 
PaymentAllocationType.PAST_DUE_PRINCIPAL, 
PaymentAllocationType.PAST_DUE_INTEREST,
+                PaymentAllocationType.DUE_PENALTY, 
PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
+                PaymentAllocationType.DUE_INTEREST, 
PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
+                PaymentAllocationType.IN_ADVANCE_PRINCIPAL, 
PaymentAllocationType.IN_ADVANCE_INTEREST);
+
+        advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
+        return advancedPaymentData;
+    }
+
+    private static AdvancedPaymentData createDefaultPaymentAllocation() {
+        AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
+        advancedPaymentData.setTransactionType("DEFAULT");
+        
advancedPaymentData.setFutureInstallmentAllocationRule("NEXT_INSTALLMENT");
+
+        List<PaymentAllocationOrder> paymentAllocationOrders = 
getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
+                PaymentAllocationType.PAST_DUE_FEE, 
PaymentAllocationType.PAST_DUE_PRINCIPAL, 
PaymentAllocationType.PAST_DUE_INTEREST,
+                PaymentAllocationType.DUE_PENALTY, 
PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
+                PaymentAllocationType.DUE_INTEREST, 
PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
+                PaymentAllocationType.IN_ADVANCE_PRINCIPAL, 
PaymentAllocationType.IN_ADVANCE_INTEREST);
+
+        advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
+        return advancedPaymentData;
+    }
+
+    private static List<PaymentAllocationOrder> 
getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
+        AtomicInteger integer = new AtomicInteger(1);
+        return Arrays.stream(paymentAllocationTypes).map(pat -> {
+            PaymentAllocationOrder paymentAllocationOrder = new 
PaymentAllocationOrder();
+            paymentAllocationOrder.setPaymentAllocationRule(pat.name());
+            paymentAllocationOrder.setOrder(integer.getAndIncrement());
+            return paymentAllocationOrder;
+        }).toList();
+    }
+}

Reply via email to