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

aleks 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 ae71df17d Charge back Loan Transaction updates repayment schedule
ae71df17d is described below

commit ae71df17df586e72a663e7069bb94a7b5dcaf2c4
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Mon Oct 3 07:59:12 2022 -0500

    Charge back Loan Transaction updates repayment schedule
---
 .../avro/loan/v1/LoanSchedulePeriodDataV1.avsc     |   8 +
 .../api/LoanTransactionsApiResourceSwagger.java    |  23 +-
 .../loanaccount/api/LoansApiResourceSwagger.java   |   2 +
 .../domain/DefaultLoanLifecycleStateMachine.java   |   5 +
 .../portfolio/loanaccount/domain/Loan.java         |  70 +++-
 .../domain/LoanRepaymentScheduleInstallment.java   |  43 +++
 .../loanaccount/domain/LoanSummaryWrapper.java     |   2 +-
 .../loanaccount/domain/LoanTransaction.java        |  14 +-
 ...tLoanRepaymentScheduleTransactionProcessor.java |  67 ++++
 .../LoanRepaymentScheduleTransactionProcessor.java |   3 +
 .../loanschedule/data/LoanScheduleData.java        |   5 +-
 .../loanschedule/data/LoanSchedulePeriodData.java  |  14 +-
 .../loanschedule/domain/LoanScheduleModel.java     |   4 +-
 .../rescheduleloan/domain/LoanRescheduleModel.java |   4 +-
 .../service/LoanReadPlatformServiceImpl.java       |  73 ++--
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |  44 ++-
 .../db/changelog/tenant/changelog-tenant.xml       |   1 +
 ...4_additional_fields_loan_repayment_schedule.xml |  36 ++
 .../DelinquencyBucketsIntegrationTest.java         |   4 +-
 .../LoanTransactionChargebackTest.java             | 379 +++++++++++++++++++--
 .../common/loans/LoanTransactionHelper.java        |  47 ++-
 21 files changed, 749 insertions(+), 99 deletions(-)

diff --git 
a/fineract-avro-schemas/src/main/avro/loan/v1/LoanSchedulePeriodDataV1.avsc 
b/fineract-avro-schemas/src/main/avro/loan/v1/LoanSchedulePeriodDataV1.avsc
index f7a0dd15e..cb6b8492f 100644
--- a/fineract-avro-schemas/src/main/avro/loan/v1/LoanSchedulePeriodDataV1.avsc
+++ b/fineract-avro-schemas/src/main/avro/loan/v1/LoanSchedulePeriodDataV1.avsc
@@ -322,6 +322,14 @@
                 "null",
                 "bigdecimal"
             ]
+        },
+        {
+            "default": null,
+            "name": "totalCredits",
+            "type": [
+                "null",
+                "bigdecimal"
+            ]
         }
     ]
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
index 8ca36db06..0aa23eda2 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.api;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import java.time.LocalDate;
+import java.util.Set;
 
 /**
  * Created by Chirag Gupta on 12/30/17.
@@ -128,6 +129,23 @@ final class LoanTransactionsApiResourceSwagger {
             public String displayLabel;
         }
 
+        static final class GetLoanTransactionRelation {
+
+            private GetLoanTransactionRelation() {}
+
+            @Schema(example = "1")
+            public Long fromLoanTransaction;
+            @Schema(example = "10")
+            public Long toLoanTransaction;
+            @Schema(example = "CHARGEBACK")
+            private String relationType;
+            @Schema(example = "100.00")
+            private Double amount;
+            @Schema(example = "Repayment Adjustment Chargeback")
+            private String paymentType;
+
+        }
+
         @Schema(example = "3")
         public Integer id;
         public GetLoansType type;
@@ -146,6 +164,7 @@ final class LoanTransactionsApiResourceSwagger {
         public String reversalExternalId;
         @Schema(example = "[2012, 5, 18]")
         public LocalDate reversedOnDate;
+        public Set<GetLoanTransactionRelation> transactionRelations;
     }
 
     @Schema(description = "PostLoansLoanIdTransactionsRequest")
@@ -210,9 +229,9 @@ final class LoanTransactionsApiResourceSwagger {
         public String note;
         @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
         public String reversalExternalId;
-        @Schema(example = "3")
+        @Schema(example = "1")
         public Integer paymentTypeId;
-        @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
+        @Schema(example = "4ff9b1cb988b7")
         public String externalId;
     }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index 1cffd9d25..98bc435ff 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -350,6 +350,8 @@ final class LoansApiResourceSwagger {
             public Double totalActualCostOfLoanForPeriod;
             @Schema(example = "200.000000")
             public Double totalInstallmentAmountForPeriod;
+            @Schema(example = "2.000000")
+            public Double totalCredits;
         }
 
         static final class GetLoansLoanIdDisbursementDetails {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java
index 8f1219397..5c34901bc 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java
@@ -151,6 +151,11 @@ public class DefaultLoanLifecycleStateMachine implements 
LoanLifecycleStateMachi
             case LOAN_CREDIT_BALANCE_REFUND:
                 newState = closeObligationsMetTransition();
             break;
+            case LOAN_CHARGEBACK:
+                if (anyOfAllowedWhenComingFrom(from, 
LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
+                    newState = activeTransition();
+                }
+            break;
             default:
             break;
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index d70c6ab28..c84784782 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -1330,7 +1330,10 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
             this.totalOverpaid = null;
         } else {
             final Money overpaidBy = calculateTotalOverpayment();
-            this.totalOverpaid = overpaidBy.getAmountDefaultedToNullIfZero();
+            this.totalOverpaid = null;
+            if (!overpaidBy.isLessThanZero()) {
+                this.totalOverpaid = 
overpaidBy.getAmountDefaultedToNullIfZero();
+            }
 
             final Money recoveredAmount = calculateTotalRecoveredPayments();
             this.totalRecovered = 
recoveredAmount.getAmountDefaultedToNullIfZero();
@@ -1342,7 +1345,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         }
     }
 
-    public void updateLoanSummarAndStatus() {
+    public void updateLoanSummaryAndStatus() {
         updateLoanSummaryDerivedFields();
         doPostLoanTransactionChecks(getLastUserTransactionDate(), 
loanLifecycleStateMachine);
     }
@@ -3362,14 +3365,18 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         return transactions;
     }
 
-    private void doPostLoanTransactionChecks(final LocalDate transactionDate, 
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
-
+    private boolean doPostLoanTransactionChecks(final LocalDate 
transactionDate,
+            final LoanLifecycleStateMachine loanLifecycleStateMachine) {
+        boolean statusChanged = false;
         if (isOverPaid()) {
             // FIXME - kw - update account balance to negative amount.
             handleLoanOverpayment(loanLifecycleStateMachine);
+            statusChanged = true;
         } else if (this.summary.isRepaidInFull(loanCurrency())) {
             handleLoanRepaymentInFull(transactionDate, 
loanLifecycleStateMachine);
+            statusChanged = true;
         }
+        return statusChanged;
     }
 
     private void handleLoanRepaymentInFull(final LocalDate transactionDate, 
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
@@ -3689,9 +3696,11 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         }
 
         for (final LoanTransaction loanTransaction : this.loanTransactions) {
-            if ((loanTransaction.isRefund() || 
loanTransaction.isRefundForActiveLoan() || 
loanTransaction.isCreditBalanceRefund())
-                    && !loanTransaction.isReversed()) {
-                totalPaidInRepayments = 
totalPaidInRepayments.minus(loanTransaction.getAmount(currency));
+            if (!loanTransaction.isReversed()) {
+                if ((loanTransaction.isRefund() || 
loanTransaction.isRefundForActiveLoan() || 
loanTransaction.isCreditBalanceRefund()
+                        || loanTransaction.isChargeback())) {
+                    totalPaidInRepayments = 
totalPaidInRepayments.minus(loanTransaction.getAmount(currency));
+                }
             }
         }
 
@@ -3898,12 +3907,6 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         return changedTransactionDetail;
     }
 
-    public void handleChargebackTransaction(LoanTransaction 
chargebackTransaction,
-            final LoanLifecycleStateMachine loanLifecycleStateMachine) {
-        chargebackTransaction.updateLoan(this);
-        // TODO To be updated in the repayment schedule
-    }
-
     /**
      * Behaviour added to comply with capability of previous mifos product to 
support easier transition to fineract
      * platform.
@@ -5678,6 +5681,14 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
             if (loanTransaction.isDisbursement() || 
loanTransaction.isIncomePosting()) {
                 outstanding = 
outstanding.plus(loanTransaction.getAmount(getCurrency()));
                 
loanTransaction.updateOutstandingLoanBalance(outstanding.getAmount());
+            } else if (loanTransaction.isChargeback()) {
+                Money transactionOutstanding = 
loanTransaction.getAmount(getCurrency());
+                if 
(!loanTransaction.getOverPaymentPortion(getCurrency()).isZero()) {
+                    transactionOutstanding = 
transactionOutstanding.minus(loanTransaction.getOverPaymentPortion(getCurrency()));
+                }
+                outstanding = outstanding.plus(transactionOutstanding);
+                
loanTransaction.updateOutstandingLoanBalance(outstanding.getAmount());
+
             } else {
                 if (this.loanInterestRecalculationDetails != null
                         && 
this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
@@ -6125,6 +6136,39 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         return changedTransactionDetail;
     }
 
+    public void handleChargebackTransaction(final LoanTransaction 
chargebackTransaction,
+            final LoanLifecycleStateMachine loanLifecycleStateMachine) {
+
+        chargebackTransaction.updateLoan(this);
+
+        if (!chargebackTransaction.isChargeback()) {
+            final String errorMessage = "A transaction of type chargeback was 
expected but not received.";
+            throw new InvalidLoanTransactionTypeException("transaction", 
"is.not.a.chargeback.transaction", errorMessage);
+        }
+
+        final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
+                .determineProcessor(this.transactionProcessingStrategy);
+        final Money overpaidAmount = calculateTotalOverpayment(); // Before 
Transaction
+        if (overpaidAmount.isGreaterThanZero()) {
+            Money difference = 
chargebackTransaction.getAmount(getCurrency()).minus(overpaidAmount);
+            if (difference.isLessThanZero()) {
+                difference = null;
+            }
+            chargebackTransaction.setOverPayments(difference);
+        }
+
+        if (chargebackTransaction.isNotZero(loanCurrency())) {
+            addLoanTransaction(chargebackTransaction);
+        }
+        
loanRepaymentScheduleTransactionProcessor.handleChargeback(chargebackTransaction,
 getCurrency(), overpaidAmount,
+                getRepaymentScheduleInstallments());
+
+        updateLoanSummaryDerivedFields();
+        if 
(!doPostLoanTransactionChecks(chargebackTransaction.getTransactionDate(), 
loanLifecycleStateMachine)) {
+            loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGEBACK, 
this);
+        }
+    }
+
     public LocalDate possibleNextRefundDate() {
 
         final LocalDate now = DateUtils.getBusinessLocalDate();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
index 078e051b0..47d57fc93 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
@@ -126,6 +126,12 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
     @Column(name = "recalculated_interest_component", nullable = false)
     private boolean recalculatedInterestComponent;
 
+    @Column(name = "is_additional", nullable = false)
+    private boolean additional;
+
+    @Column(name = "credits_amount", scale = 6, precision = 19, nullable = 
true)
+    private BigDecimal credits;
+
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = 
FetchType.EAGER, mappedBy = "loanRepaymentScheduleInstallment")
     private Set<LoanInterestRecalcualtionAdditionalDetails> 
loanCompoundingDetails = new HashSet<>();
 
@@ -223,6 +229,10 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
         return this.dueDate;
     }
 
+    public Money getCredits(final MonetaryCurrency currency) {
+        return Money.of(currency, this.credits);
+    }
+
     public Money getPrincipal(final MonetaryCurrency currency) {
         return Money.of(currency, this.principal);
     }
@@ -688,6 +698,24 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
         this.principal = principal;
     }
 
+    public void addToPrincipal(final LocalDate transactionDate, final Money 
transactionAmount) {
+        if (this.principal == null) {
+            this.principal = transactionAmount.getAmount();
+        } else {
+            this.principal = this.principal.add(transactionAmount.getAmount());
+        }
+        checkIfRepaymentPeriodObligationsAreMet(transactionDate, 
transactionAmount.getCurrency());
+
+    }
+
+    public void addToCredits(final BigDecimal amount) {
+        if (this.credits == null) {
+            this.credits = amount;
+        } else {
+            this.credits = this.credits.add(amount);
+        }
+    }
+
     public static Comparator<LoanRepaymentScheduleInstallment> 
installmentNumberComparator = new 
Comparator<LoanRepaymentScheduleInstallment>() {
 
         @Override
@@ -816,6 +844,12 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
         }
     }
 
+    public void updateDueChargeback(final LocalDate transactionDate, final 
Money transactionAmount) {
+        updateDueDate(transactionDate);
+        addToCredits(transactionAmount.getAmount());
+        addToPrincipal(transactionDate, transactionAmount);
+    }
+
     public Money getDue(MonetaryCurrency currency) {
         return 
getPrincipal(currency).plus(getInterestCharged(currency)).plus(getFeeChargesCharged(currency))
                 .plus(getPenaltyChargesCharged(currency));
@@ -851,4 +885,13 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
     public Set<LoanInstallmentCharge> getInstallmentCharges() {
         return installmentCharges;
     }
+
+    public boolean isAdditional() {
+        return additional;
+    }
+
+    public void markAsAdditional() {
+        this.additional = true;
+    }
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanSummaryWrapper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanSummaryWrapper.java
index 0c8b82fb1..437eead01 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanSummaryWrapper.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanSummaryWrapper.java
@@ -35,7 +35,7 @@ public final class LoanSummaryWrapper {
             final MonetaryCurrency currency) {
         Money total = Money.zero(currency);
         for (final LoanRepaymentScheduleInstallment installment : 
repaymentScheduleInstallments) {
-            total = total.plus(installment.getPrincipalCompleted(currency));
+            total = 
total.plus(installment.getPrincipalCompleted(currency)).minus(installment.getCredits(currency));
         }
         return total;
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index 22313cc2b..82d0da802 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -158,9 +158,9 @@ public class LoanTransaction extends 
AbstractAuditableWithUTCDateTimeCustom {
         return new LoanTransaction(null, office, 
LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, 
externalId);
     }
 
-    public static LoanTransaction chargeback(final Loan loan, final Money 
amount, final PaymentDetail paymentDetail,
+    public static LoanTransaction chargeback(final Office office, final Money 
amount, final PaymentDetail paymentDetail,
             final LocalDate paymentDate, final String externalId) {
-        LoanTransaction loanTransaction = new LoanTransaction(loan, 
loan.getOffice(), LoanTransactionType.CHARGEBACK, paymentDetail,
+        LoanTransaction loanTransaction = new LoanTransaction(null, office, 
LoanTransactionType.CHARGEBACK, paymentDetail,
                 amount.getAmount(), paymentDate, externalId);
         loanTransaction.principalPortion = amount.getAmount();
         return loanTransaction;
@@ -459,6 +459,12 @@ public class LoanTransaction extends 
AbstractAuditableWithUTCDateTimeCustom {
         this.overPaymentPortion = 
defaultToNullIfZero(getOverPaymentPortion(currency).plus(overPayment).getAmount());
     }
 
+    public void setOverPayments(final Money overPayment) {
+        if (overPayment != null) {
+            this.overPaymentPortion = 
defaultToNullIfZero(overPayment.getAmount());
+        }
+    }
+
     public Money getPrincipalPortion(final MonetaryCurrency currency) {
         return Money.of(currency, this.principalPortion);
     }
@@ -863,6 +869,10 @@ public class LoanTransaction extends 
AbstractAuditableWithUTCDateTimeCustom {
         return submittedOnDate;
     }
 
+    public boolean hasLoanTransactionRelations() {
+        return (loanTransactionRelations != null && 
loanTransactionRelations.size() > 0);
+    }
+
     public Set<LoanTransactionRelation> getLoanTransactionRelations() {
         return loanTransactionRelations;
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
index 5a10c2298..acf79de72 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -30,9 +30,11 @@ import 
org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidDetail;
 import 
org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -505,6 +507,71 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         return false;
     }
 
+    @Override
+    public void handleChargeback(LoanTransaction loanTransaction, 
MonetaryCurrency currency, Money overpaidAmount,
+            List<LoanRepaymentScheduleInstallment> installments) {
+        List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = 
new ArrayList<>();
+        final Comparator<LoanRepaymentScheduleInstallment> byDate = new 
Comparator<LoanRepaymentScheduleInstallment>() {
+
+            @Override
+            public int compare(LoanRepaymentScheduleInstallment ord1, 
LoanRepaymentScheduleInstallment ord2) {
+                return ord1.getDueDate().compareTo(ord2.getDueDate());
+            }
+        };
+        Collections.sort(installments, byDate);
+        final Money zeroMoney = Money.zero(currency);
+        Money transactionAmountUnprocessed = 
loanTransaction.getAmount(currency);
+        if (overpaidAmount.isGreaterThanZero()) {
+            transactionAmountUnprocessed = 
loanTransaction.getAmount(currency).minus(overpaidAmount);
+            if (transactionAmountUnprocessed.isLessThanZero()) {
+                transactionAmountUnprocessed = zeroMoney;
+            }
+        }
+
+        if (transactionAmountUnprocessed.isGreaterThanZero()) {
+            final LocalDate transactionDate = 
loanTransaction.getTransactionDate();
+            boolean loanTransactionMapped = false;
+            LocalDate pastDueDate = null;
+            for (final LoanRepaymentScheduleInstallment currentInstallment : 
installments) {
+                pastDueDate = currentInstallment.getDueDate();
+                if (!currentInstallment.isAdditional() && 
currentInstallment.getDueDate().isAfter(transactionDate)) {
+                    currentInstallment.addToPrincipal(transactionDate, 
transactionAmountUnprocessed);
+                    
currentInstallment.addToCredits(transactionAmountUnprocessed.getAmount());
+                    
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
 currentInstallment,
+                            transactionAmountUnprocessed, zeroMoney, 
zeroMoney, zeroMoney));
+
+                    loanTransactionMapped = true;
+
+                    break;
+
+                    // If already exists an additional installment just update 
the due date and
+                    // principal from the Loan charge back transaction
+                } else if (currentInstallment.isAdditional()) {
+                    currentInstallment.updateDueChargeback(transactionDate, 
transactionAmountUnprocessed);
+                    loanTransactionMapped = true;
+                    break;
+                }
+            }
+
+            // New installment will be added (N+1 scenario)
+            if (!loanTransactionMapped) {
+                Loan loan = loanTransaction.getLoan();
+                final Set<LoanInterestRecalcualtionAdditionalDetails> 
compoundingDetails = null;
+                LoanRepaymentScheduleInstallment installment = new 
LoanRepaymentScheduleInstallment(loan, (installments.size() + 1),
+                        pastDueDate, transactionDate, 
transactionAmountUnprocessed.getAmount(), zeroMoney.getAmount(),
+                        zeroMoney.getAmount(), zeroMoney.getAmount(), false, 
compoundingDetails);
+                installment.markAsAdditional();
+                
installment.addToCredits(transactionAmountUnprocessed.getAmount());
+                loan.addLoanRepaymentScheduleInstallment(installment);
+
+                
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
 installment,
+                        transactionAmountUnprocessed, zeroMoney, zeroMoney, 
zeroMoney));
+            }
+
+            
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
+        }
+    }
+
     @Override
     public void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency 
currency,
             List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
index c262897df..b65d2d28a 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
@@ -50,6 +50,9 @@ public interface LoanRepaymentScheduleTransactionProcessor {
     void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency 
currency, List<LoanRepaymentScheduleInstallment> installments,
             Set<LoanCharge> charges);
 
+    void handleChargeback(LoanTransaction loanTransaction, MonetaryCurrency 
currency, Money overpaidAmount,
+            List<LoanRepaymentScheduleInstallment> installments);
+
     void processTransactionsFromDerivedFields(List<LoanTransaction> 
transactionsPostDisbursement, MonetaryCurrency currency,
             List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> charges);
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleData.java
index 3c9ea4e19..4f7a4d153 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanScheduleData.java
@@ -53,6 +53,7 @@ public class LoanScheduleData {
     private final BigDecimal totalPaidInAdvance;
     private final BigDecimal totalPaidLate;
     private final BigDecimal totalOutstanding;
+    private final BigDecimal totalCredits;
 
     /**
      * <code>periods</code> is collection of data objects containing specific 
information to each period of the loan
@@ -67,7 +68,7 @@ public class LoanScheduleData {
             final BigDecimal totalInterestCharged, final BigDecimal 
totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged,
             final BigDecimal totalWaived, final BigDecimal totalWrittenOff, 
final BigDecimal totalRepaymentExpected,
             final BigDecimal totalRepayment, final BigDecimal 
totalPaidInAdvance, final BigDecimal totalPaidLate,
-            final BigDecimal totalOutstanding) {
+            final BigDecimal totalOutstanding, final BigDecimal totalCredits) {
         this.currency = currency;
         this.periods = periods;
         this.loanTermInDays = loanTermInDays;
@@ -84,6 +85,7 @@ public class LoanScheduleData {
         this.totalPaidInAdvance = totalPaidInAdvance;
         this.totalPaidLate = totalPaidLate;
         this.totalOutstanding = totalOutstanding;
+        this.totalCredits = totalCredits;
     }
 
     public LoanScheduleData(final CurrencyData currency, final 
Collection<LoanSchedulePeriodData> periods, final Integer loanTermInDays,
@@ -105,6 +107,7 @@ public class LoanScheduleData {
         this.totalPaidInAdvance = null;
         this.totalPaidLate = null;
         this.totalOutstanding = null;
+        this.totalCredits = BigDecimal.ZERO;
     }
 
     public void updateFuturePeriods(Collection<LoanSchedulePeriodData> 
futurePeriods) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
index b03f44735..fc74cbfa7 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
@@ -71,6 +71,7 @@ public final class LoanSchedulePeriodData {
     private final BigDecimal totalOverdue;
     private final BigDecimal totalActualCostOfLoanForPeriod;
     private final BigDecimal totalInstallmentAmountForPeriod;
+    private final BigDecimal totalCredits;
 
     public static LoanSchedulePeriodData disbursementOnlyPeriod(final 
LocalDate disbursementDate, final BigDecimal principalDisbursed,
             final BigDecimal feeChargesDueAtTimeOfDisbursement, final boolean 
isDisbursed) {
@@ -102,7 +103,7 @@ public final class LoanSchedulePeriodData {
             final BigDecimal totalDueForPeriod, final BigDecimal totalPaid, 
final BigDecimal totalPaidInAdvanceForPeriod,
             final BigDecimal totalPaidLateForPeriod, final BigDecimal 
totalWaived, final BigDecimal totalWrittenOff,
             final BigDecimal totalOutstanding, final BigDecimal 
totalActualCostOfLoanForPeriod,
-            final BigDecimal totalInstallmentAmountForPeriod) {
+            final BigDecimal totalInstallmentAmountForPeriod, final BigDecimal 
totalCredits) {
 
         return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, 
obligationsMetOnDate, complete, principalOriginalDue,
                 principalPaid, principalWrittenOff, principalOutstanding, 
outstandingPrincipalBalanceOfLoan,
@@ -110,7 +111,7 @@ public final class LoanSchedulePeriodData {
                 feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, 
feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
                 penaltyChargesWaived, penaltyChargesWrittenOff, 
penaltyChargesOutstanding, totalDueForPeriod, totalPaid,
                 totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, 
totalWaived, totalWrittenOff, totalOutstanding,
-                totalActualCostOfLoanForPeriod, 
totalInstallmentAmountForPeriod);
+                totalActualCostOfLoanForPeriod, 
totalInstallmentAmountForPeriod, totalCredits);
     }
 
     public static LoanSchedulePeriodData withPaidDetail(final 
LoanSchedulePeriodData loanSchedulePeriodData, final boolean complete,
@@ -130,7 +131,8 @@ public final class LoanSchedulePeriodData {
                 loanSchedulePeriodData.totalPaidForPeriod, 
loanSchedulePeriodData.totalPaidInAdvanceForPeriod,
                 loanSchedulePeriodData.totalPaidLateForPeriod, 
loanSchedulePeriodData.totalWaivedForPeriod,
                 loanSchedulePeriodData.totalWrittenOffForPeriod, 
loanSchedulePeriodData.totalOutstandingForPeriod,
-                loanSchedulePeriodData.totalActualCostOfLoanForPeriod, 
loanSchedulePeriodData.totalInstallmentAmountForPeriod);
+                loanSchedulePeriodData.totalActualCostOfLoanForPeriod, 
loanSchedulePeriodData.totalInstallmentAmountForPeriod,
+                loanSchedulePeriodData.totalCredits);
     }
 
     /*
@@ -197,6 +199,7 @@ public final class LoanSchedulePeriodData {
         } else {
             this.totalOverdue = null;
         }
+        this.totalCredits = BigDecimal.ZERO;
     }
 
     /*
@@ -259,6 +262,7 @@ public final class LoanSchedulePeriodData {
         } else {
             this.totalOverdue = null;
         }
+        this.totalCredits = BigDecimal.ZERO;
     }
 
     /*
@@ -276,7 +280,8 @@ public final class LoanSchedulePeriodData {
             final BigDecimal penaltyChargesWrittenOff, final BigDecimal 
penaltyChargesOutstanding, final BigDecimal totalDueForPeriod,
             final BigDecimal totalPaid, final BigDecimal 
totalPaidInAdvanceForPeriod, final BigDecimal totalPaidLateForPeriod,
             final BigDecimal totalWaived, final BigDecimal totalWrittenOff, 
final BigDecimal totalOutstanding,
-            final BigDecimal totalActualCostOfLoanForPeriod, final BigDecimal 
totalInstallmentAmountForPeriod) {
+            final BigDecimal totalActualCostOfLoanForPeriod, final BigDecimal 
totalInstallmentAmountForPeriod,
+            final BigDecimal totalCredits) {
         this.period = periodNumber;
         this.fromDate = fromDate;
         this.dueDate = dueDate;
@@ -330,6 +335,7 @@ public final class LoanSchedulePeriodData {
         } else {
             this.totalOverdue = null;
         }
+        this.totalCredits = totalCredits;
     }
 
     private BigDecimal defaultToZeroIfNull(final BigDecimal possibleNullValue) 
{
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java
index c38636cdd..e7e8d92c2 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java
@@ -95,6 +95,8 @@ public final class LoanScheduleModel {
         final Integer inMultiplesOf = 
this.totalPrincipalDisbursed.getCurrencyInMultiplesOf();
         final CurrencyData currency = 
this.applicationCurrency.toData(decimalPlaces, inMultiplesOf);
 
+        final BigDecimal totalCredits = BigDecimal.ZERO;
+
         final Collection<LoanSchedulePeriodData> periodsData = new 
ArrayList<>();
         for (final LoanScheduleModelPeriod modelPeriod : this.periods) {
             periodsData.add(modelPeriod.toData());
@@ -109,7 +111,7 @@ public final class LoanScheduleModel {
         return new LoanScheduleData(currency, periodsData, 
this.loanTermInDays, this.totalPrincipalDisbursed.getAmount(),
                 this.totalPrincipalExpected, this.totalPrincipalPaid, 
this.totalInterestCharged, this.totalFeeChargesCharged,
                 this.totalPenaltyChargesCharged, totalWaived, totalWrittenOff, 
this.totalRepaymentExpected, totalRepayment,
-                totalPaidInAdvance, totalPaidLate, this.totalOutstanding);
+                totalPaidInAdvance, totalPaidLate, this.totalOutstanding, 
totalCredits);
     }
 
     public Collection<LoanScheduleModelPeriod> getPeriods() {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleModel.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleModel.java
index 9f87bc32e..f9ec64f97 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleModel.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleModel.java
@@ -89,6 +89,8 @@ public final class LoanRescheduleModel {
         final Integer inMultiplesOf = 
this.totalPrincipalDisbursed.getCurrencyInMultiplesOf();
         final CurrencyData currency = 
this.applicationCurrency.toData(decimalPlaces, inMultiplesOf);
 
+        final BigDecimal totalCredits = BigDecimal.ZERO;
+
         final Collection<LoanSchedulePeriodData> periodsData = new 
ArrayList<>();
         for (final LoanRescheduleModalPeriod modelPeriod : this.periods) {
             periodsData.add(modelPeriod.toData());
@@ -103,7 +105,7 @@ public final class LoanRescheduleModel {
         return new LoanScheduleData(currency, periodsData, 
this.loanTermInDays, this.totalPrincipalDisbursed.getAmount(),
                 this.totalPrincipalExpected, this.totalPrincipalPaid, 
this.totalInterestCharged, this.totalFeeChargesCharged,
                 this.totalPenaltyChargesCharged, totalWaived, totalWrittenOff, 
this.totalRepaymentExpected, totalRepayment,
-                totalPaidInAdvance, totalPaidLate, this.totalOutstanding);
+                totalPaidInAdvance, totalPaidLate, this.totalOutstanding, 
totalCredits);
     }
 
     public Collection<LoanRescheduleModelRepaymentPeriod> getPeriods() {
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 7005b8adb..54d901578 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
@@ -1078,11 +1078,12 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
         public String schema() {
 
             return " ls.loan_id as loanId, ls.installment as period, 
ls.fromdate as fromDate, ls.duedate as dueDate, ls.obligations_met_on_date as 
obligationsMetOnDate, ls.completed_derived as complete,"
-                    + " ls.principal_amount as principalDue, 
ls.principal_completed_derived as principalPaid, 
ls.principal_writtenoff_derived as principalWrittenOff, "
+                    + " ls.principal_amount as principalDue, 
ls.principal_completed_derived as principalPaid, 
ls.principal_writtenoff_derived as principalWrittenOff, ls.is_additional as 
isAdditional, "
                     + " ls.interest_amount as interestDue, 
ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as 
interestWaived, ls.interest_writtenoff_derived as interestWrittenOff, "
                     + " ls.fee_charges_amount as feeChargesDue, 
ls.fee_charges_completed_derived as feeChargesPaid, 
ls.fee_charges_waived_derived as feeChargesWaived, 
ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, "
-                    + " ls.penalty_charges_amount as penaltyChargesDue, 
ls.penalty_charges_completed_derived as penaltyChargesPaid, 
ls.penalty_charges_waived_derived as penaltyChargesWaived, 
ls.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff, "
-                    + " ls.total_paid_in_advance_derived as 
totalPaidInAdvanceForPeriod, ls.total_paid_late_derived as 
totalPaidLateForPeriod "
+                    + " ls.penalty_charges_amount as penaltyChargesDue, 
ls.penalty_charges_completed_derived as penaltyChargesPaid, 
ls.penalty_charges_waived_derived as penaltyChargesWaived, "
+                    + " ls.penalty_charges_writtenoff_derived as 
penaltyChargesWrittenOff, ls.total_paid_in_advance_derived as 
totalPaidInAdvanceForPeriod, "
+                    + " ls.total_paid_late_derived as totalPaidLateForPeriod, 
ls.credits_amount as totalCredits "
                     + " from m_loan_repayment_schedule ls ";
         }
 
@@ -1128,6 +1129,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
             Money totalPaidInAdvance = Money.zero(monCurrency);
             Money totalPaidLate = Money.zero(monCurrency);
             Money totalOutstanding = Money.zero(monCurrency);
+            Money totalCredits = Money.zero(monCurrency);
 
             // update totals with details of fees charged during disbursement
             totalFeeChargesCharged = 
totalFeeChargesCharged.plus(disbursementPeriod.getFeeChargesDue().subtract(waivedChargeAmount));
@@ -1144,37 +1146,47 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                 final LocalDate dueDate = JdbcSupport.getLocalDate(rs, 
"dueDate");
                 final LocalDate obligationsMetOnDate = 
JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
                 final boolean complete = rs.getBoolean("complete");
+                final boolean isAdditional = rs.getBoolean("isAdditional");
                 BigDecimal principal = BigDecimal.ZERO;
-                for (final DisbursementData data : disbursementData) {
-                    if (fromDate.equals(this.disbursement.disbursementDate()) 
&& data.disbursementDate().equals(fromDate)) {
-                        principal = principal.add(data.getPrincipal());
-                        LoanSchedulePeriodData periodData = null;
-                        if (data.getChargeAmount() == null) {
-                            periodData = 
LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), 
data.getPrincipal(),
-                                    disbursementChargeAmount, 
data.isDisbursed());
-                        } else {
-                            periodData = 
LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), 
data.getPrincipal(),
-                                    
disbursementChargeAmount.add(data.getChargeAmount()).subtract(waivedChargeAmount),
 data.isDisbursed());
-                        }
-                        periods.add(periodData);
-                        this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
-                    } else if (data.isDueForDisbursement(fromDate, dueDate)) {
-                        if (!excludePastUndisbursed || data.isDisbursed()
-                                || 
!data.disbursementDate().isBefore(DateUtils.getBusinessLocalDate())) {
+                if (!isAdditional) {
+                    for (final DisbursementData data : disbursementData) {
+                        if 
(fromDate.equals(this.disbursement.disbursementDate()) && 
data.disbursementDate().equals(fromDate)) {
                             principal = principal.add(data.getPrincipal());
-                            LoanSchedulePeriodData periodData;
+                            LoanSchedulePeriodData periodData = null;
                             if (data.getChargeAmount() == null) {
                                 periodData = 
LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), 
data.getPrincipal(),
-                                        BigDecimal.ZERO, data.isDisbursed());
+                                        disbursementChargeAmount, 
data.isDisbursed());
                             } else {
                                 periodData = 
LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), 
data.getPrincipal(),
-                                        data.getChargeAmount(), 
data.isDisbursed());
+                                        
disbursementChargeAmount.add(data.getChargeAmount()).subtract(waivedChargeAmount),
+                                        data.isDisbursed());
                             }
                             periods.add(periodData);
                             this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
+                        } else if (data.isDueForDisbursement(fromDate, 
dueDate)) {
+                            if (!excludePastUndisbursed || data.isDisbursed()
+                                    || 
!data.disbursementDate().isBefore(DateUtils.getBusinessLocalDate())) {
+                                principal = principal.add(data.getPrincipal());
+                                LoanSchedulePeriodData periodData;
+                                if (data.getChargeAmount() == null) {
+                                    periodData = 
LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), 
data.getPrincipal(),
+                                            BigDecimal.ZERO, 
data.isDisbursed());
+                                } else {
+                                    periodData = 
LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), 
data.getPrincipal(),
+                                            data.getChargeAmount(), 
data.isDisbursed());
+                                }
+                                periods.add(periodData);
+                                this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
+                            }
                         }
                     }
                 }
+                // Add the Charge back or Credits to the initial amount to 
avoid negative balance
+                final BigDecimal credits = 
JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalCredits");
+                if (!isAdditional) {
+                    this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.add(credits);
+                }
+
                 totalPrincipalDisbursed = 
totalPrincipalDisbursed.add(principal);
 
                 Integer daysInPeriod = 0;
@@ -1235,6 +1247,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                 final BigDecimal totalWrittenOffForPeriod = 
principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff)
                         .add(penaltyChargesWrittenOff);
                 totalWrittenOff = 
totalWrittenOff.plus(totalWrittenOffForPeriod);
+
                 final BigDecimal totalOutstandingForPeriod = 
principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding)
                         .add(penaltyChargesOutstanding);
 
@@ -1249,11 +1262,18 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                 if (fromDate == null) {
                     fromDate = this.lastDueDate;
                 }
-                final BigDecimal outstandingPrincipalBalanceOfLoan = 
this.outstandingLoanPrincipalBalance.subtract(principalDue);
+
+                BigDecimal outstandingPrincipalBalanceOfLoan = 
this.outstandingLoanPrincipalBalance.subtract(principalDue);
+                if (isAdditional) {
+                    outstandingPrincipalBalanceOfLoan = 
this.outstandingLoanPrincipalBalance.add(principalDue);
+                }
 
                 // update based on current period values
                 this.lastDueDate = dueDate;
                 this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.subtract(principalDue);
+                if (isAdditional) {
+                    this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.add(principalDue);
+                }
 
                 final LoanSchedulePeriodData periodData = 
LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate,
                         dueDate, obligationsMetOnDate, complete, principalDue, 
principalPaid, principalWrittenOff, principalOutstanding,
@@ -1262,7 +1282,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                         feeChargesOutstanding, penaltyChargesExpectedDue, 
penaltyChargesPaid, penaltyChargesWaived,
                         penaltyChargesWrittenOff, penaltyChargesOutstanding, 
totalDueForPeriod, totalPaidForPeriod,
                         totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, 
totalWaivedForPeriod, totalWrittenOffForPeriod,
-                        totalOutstandingForPeriod, 
totalActualCostOfLoanForPeriod, totalInstallmentAmount);
+                        totalOutstandingForPeriod, 
totalActualCostOfLoanForPeriod, totalInstallmentAmount, credits);
 
                 periods.add(periodData);
             }
@@ -1271,7 +1291,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                     totalPrincipalPaid.getAmount(), 
totalInterestCharged.getAmount(), totalFeeChargesCharged.getAmount(),
                     totalPenaltyChargesCharged.getAmount(), 
totalWaived.getAmount(), totalWrittenOff.getAmount(),
                     totalRepaymentExpected.getAmount(), 
totalRepayment.getAmount(), totalPaidInAdvance.getAmount(),
-                    totalPaidLate.getAmount(), totalOutstanding.getAmount());
+                    totalPaidLate.getAmount(), totalOutstanding.getAmount(), 
totalCredits.getAmount());
         }
 
     }
@@ -2142,6 +2162,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
             final BigDecimal totalOutstanding = null;
             final BigDecimal totalPaid = null;
             final BigDecimal totalInstallmentAmount = null;
+            final BigDecimal totalCredits = null;
 
             return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, 
period, fromDate, dueDate, obligationsMetOnDate, complete,
                     principalOriginalDue, principalPaid, principalWrittenOff, 
principalOutstanding, outstandingPrincipalBalanceOfLoan,
@@ -2149,7 +2170,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                     feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, 
feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
                     penaltyChargesWaived, penaltyChargesWrittenOff, 
penaltyChargesOutstanding, totalDueForPeriod, totalPaid,
                     totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, 
totalWaived, totalWrittenOff, totalOutstanding,
-                    totalActualCostOfLoanForPeriod, totalInstallmentAmount);
+                    totalActualCostOfLoanForPeriod, totalInstallmentAmount, 
totalCredits);
         }
     }
 
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 7d010b191..0ee373287 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
@@ -1074,6 +1074,18 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
                     "Loan transaction:" + transactionId + " update not allowed 
as loan status is written off", transactionId);
         }
 
+        if (transactionToAdjust.hasLoanTransactionRelations()) {
+            throw new 
PlatformServiceUnavailableException("error.msg.loan.transaction.update.not.allowed",
+                    "Loan transaction:" + transactionId + " update not allowed 
as loan transaction is linked to other transactions",
+                    transactionId);
+        }
+
+        if (transactionToAdjust.isChargeback()) {
+            throw new 
PlatformServiceUnavailableException("error.msg.loan.transaction.update.not.allowed",
 "Loan transaction:"
+                    + transactionId + " update not allowed as loan transaction 
is charge back and is linked to other transaction",
+                    transactionId);
+        }
+
         final LocalDate transactionDate = 
command.localDateValueOfParameterNamed("transactionDate");
         final BigDecimal transactionAmount = 
command.bigDecimalValueOfParameterNamed("transactionAmount");
         final String txnExternalId = 
command.stringValueOfParameterNamedAllowingNull("externalId");
@@ -1168,7 +1180,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
         if (!transactionIds.isEmpty()) {
             
this.accountTransfersWritePlatformService.reverseTransfersWithFromAccountTransactions(transactionIds,
                     PortfolioAccountType.LOAN);
-            loan.updateLoanSummarAndStatus();
+            loan.updateLoanSummaryAndStatus();
         }
 
         postJournalEntries(loan, existingTransactionIds, 
existingReversedTransactionIds);
@@ -1217,9 +1229,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
         LoanTransaction loanTransaction = 
this.loanTransactionRepository.findById(transactionId)
                 .orElseThrow(() -> new 
LoanTransactionNotFoundException(transactionId));
 
-        if (!loanTransaction.isRepayment()) {
+        if (loanTransaction.isReversed()) {
             throw new 
PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
-                    "Loan transaction:" + transactionId + " chargeback not 
allowed as loan transaction is not repayment", transactionId);
+                    "Loan transaction:" + transactionId + " chargeback not 
allowed as loan transaction repayment is reversed",
+                    transactionId);
+        }
+
+        if (!loanTransaction.isRepayment()) {
+            throw new PlatformServiceUnavailableException(
+                    "error.msg.loan.chargeback.operation.not.allowed", "Loan 
transaction:" + transactionId
+                            + " chargeback not allowed as loan transaction is 
not repayment, is " + loanTransaction.getTypeOf().getCode(),
+                    transactionId);
         }
 
         businessEventNotifierService.notifyPreBusinessEvent(new 
LoanChargebackTransactionBusinessEvent(loanTransaction));
@@ -1236,23 +1256,23 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
 
         final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), 
transactionAmount);
         final PaymentDetail paymentDetail = 
this.paymentDetailWritePlatformService.createPaymentDetail(command, changes);
-        LoanTransaction newTransaction = LoanTransaction.chargeback(loan, 
transactionAmountAsMoney, paymentDetail, transactionDate,
-                txnExternalId);
+        LoanTransaction newTransaction = 
LoanTransaction.chargeback(loan.getOffice(), transactionAmountAsMoney, 
paymentDetail,
+                transactionDate, txnExternalId);
 
         validateLoanTransactionAmountChargeBack(loanTransaction, 
newTransaction);
 
         
this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
 
-        loan.handleChargebackTransaction(newTransaction, 
defaultLoanLifecycleStateMachine());
-
-        loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
-
         // Store the Loan Transaction Relation
         LoanTransactionRelation loanTransactionRelation = 
LoanTransactionRelation.instance(loanTransaction, newTransaction,
                 LoanTransactionRelationTypeEnum.CHARGEBACK);
         this.loanTransactionRelationRepository.save(loanTransactionRelation);
 
-        this.loanTransactionRepository.saveAndFlush(newTransaction);
+        this.loanTransactionRepository.save(newTransaction);
+
+        loan.handleChargebackTransaction(newTransaction, 
defaultLoanLifecycleStateMachine());
+
+        loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
 
         final String noteText = 
command.stringValueOfParameterNamed(LoanApiConstants.noteParamName);
         if (StringUtils.isNotBlank(noteText)) {
@@ -1278,8 +1298,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
                 actualAmount = 
actualAmount.add(loanTransactionRelation.getToTransaction().getPrincipalPortion());
             }
         }
-        actualAmount = 
actualAmount.add(chargebackTransaction.getPrincipalPortion());
-        if (actualAmount.compareTo(loanTransaction.getPrincipalPortion()) > 0) 
{
+        actualAmount = actualAmount.add(chargebackTransaction.getAmount());
+        if (loanTransaction.getPrincipalPortion() != null && 
actualAmount.compareTo(loanTransaction.getPrincipalPortion()) > 0) {
             throw new 
PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
                     "Loan transaction:" + loanTransaction.getId() + " 
chargeback not allowed as loan transaction amount is not enough",
                     loanTransaction.getId());
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml 
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index 933c2b523..c7e64d63a 100644
--- 
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -73,4 +73,5 @@
     <include file="parts/0051_external_event_table_category_info.xml" 
relativeToChangelogFile="true"/>
     <include file="parts/0052_loan_transaction_chargeback.xml" 
relativeToChangelogFile="true"/>
     <include file="parts/0053_add_external_events_purge_job.xml" 
relativeToChangelogFile="true"/>
+    <include file="parts/0054_additional_fields_loan_repayment_schedule.xml" 
relativeToChangelogFile="true"/>
 </databaseChangeLog>
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0054_additional_fields_loan_repayment_schedule.xml
 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0054_additional_fields_loan_repayment_schedule.xml
new file mode 100644
index 000000000..b5d41b42b
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0054_additional_fields_loan_repayment_schedule.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog";
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+                   
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd";>
+
+    <changeSet id="1" author="fineract">
+        <addColumn tableName="m_loan_repayment_schedule">
+            <column name="is_additional" type="boolean" 
defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="m_loan_repayment_schedule">
+            <column defaultValueComputed="NULL" name="credits_amount" 
type="DECIMAL(19, 6)"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
index 17814e645..282ee48f1 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
@@ -776,8 +776,8 @@ public class DelinquencyBucketsIntegrationTest {
         log.info("Loan Id {} with Loan status {}", 
getLoansLoanIdResponse.getId(), getLoansLoanIdResponse.getStatus().getCode());
 
         // Reverse the Previous Loan Repayment
-        PostLoansLoanIdTransactionsResponse loansLoanIdReverseTransactions = 
loanTransactionHelper.reverseLoanRepayment(loanId,
-                loansLoanIdTransactions.getResourceId(), operationDate);
+        PostLoansLoanIdTransactionsResponse loansLoanIdReverseTransactions = 
loanTransactionHelper.reverseLoanTransaction(loanId,
+                loansLoanIdTransactions.getResourceId(), operationDate, 
responseSpec);
         assertNotNull(loansLoanIdReverseTransactions);
         log.info("Loan repayment reverse transaction id {}", 
loansLoanIdReverseTransactions.getResourceId());
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
index cd07d376d..aa031f3a1 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.integrationtests;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -30,12 +31,17 @@ import io.restassured.specification.ResponseSpecification;
 import java.time.LocalDate;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
+import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
+import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
 import org.apache.fineract.client.models.GetPaymentTypesResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
-import 
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
+import 
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdResponse;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.CommonConstants;
 import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
@@ -50,7 +56,12 @@ import org.junit.jupiter.api.Test;
 public class LoanTransactionChargebackTest {
 
     private ResponseSpecification responseSpec;
+    private ResponseSpecification responseSpecError;
     private RequestSpecification requestSpec;
+    private LoanTransactionHelper loanTransactionHelper;
+    private final String amountVal = "1000";
+    private LocalDate todaysDate;
+    private String operationDate;
 
     @BeforeEach
     public void setup() {
@@ -58,49 +69,342 @@ public class LoanTransactionChargebackTest {
         this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
         this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
         this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.responseSpecError = new 
ResponseSpecBuilder().expectStatusCode(503).build();
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+
+        this.todaysDate = Utils.getLocalDateOfTenant();
+        this.operationDate = Utils.dateFormatter.format(this.todaysDate);
     }
 
     @Test
     public void applyLoanTransactionChargeback() {
-        final LoanTransactionHelper loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        // Client and Loan account creation
+        final Integer loanId = createAccounts(15, 1);
+
+        GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        Float amount = Float.valueOf(amountVal);
+        PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+                loanId);
+        assertNotNull(loanIdTransactionsResponse);
+        final Integer transactionId = 
loanIdTransactionsResponse.getResourceId();
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.closed.obligations.met");
+
+        reviewLoanTransactionRelations(loanId, transactionId, 0);
+
+        final Integer chargebackTransactionId = 
applyChargebackTransaction(loanId, transactionId, "1000.00", 0, responseSpec);
+
+        reviewLoanTransactionRelations(loanId, chargebackTransactionId, 1);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.active");
+
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 amount.doubleValue());
+
+        // Try to reverse a Loan Transaction charge back
+        PostLoansLoanIdTransactionsResponse reverseTransactionResponse = 
loanTransactionHelper.reverseLoanTransaction(loanId,
+                chargebackTransactionId, operationDate, responseSpecError);
+
+        // Try to reverse a Loan Transaction repayment with linked transactions
+        reverseTransactionResponse = 
loanTransactionHelper.reverseLoanTransaction(loanId, transactionId, 
operationDate, responseSpecError);
+    }
 
+    @Test
+    public void applyLoanTransactionChargebackInLongTermLoan() {
         // Client and Loan account creation
-        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, "01 January 2012");
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProduct(loanTransactionHelper, null);
-        assertNotNull(getLoanProductsProductResponse);
-        log.info("Loan Product Bucket Name: {}", 
getLoanProductsProductResponse.getDelinquencyBucket().getName());
+        final Integer daysToSubtract = 1;
+        final Integer numberOfRepayments = 3;
+        final Integer loanId = createAccounts(daysToSubtract, 
numberOfRepayments);
 
-        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
-        // Older date to have more than one overdue installment
-        final LocalDate transactionDate = todaysDate.minusDays(45);
+        GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        Float amount = Float.valueOf("333.33");
+        final LocalDate transactionDate = 
this.todaysDate.minusMonths(numberOfRepayments - 1).plusDays(3);
         String operationDate = Utils.dateFormatter.format(transactionDate);
 
-        final Integer loanId = createLoanAccount(loanTransactionHelper, 
clientId.toString(),
-                getLoanProductsProductResponse.getId().toString(), 
operationDate, "1000");
+        PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+                loanId);
+        assertNotNull(loanIdTransactionsResponse);
+        final Integer transactionId = 
loanIdTransactionsResponse.getResourceId();
+        reviewLoanTransactionRelations(loanId, transactionId, 0);
+
+        final Integer chargebackTransactionId = 
applyChargebackTransaction(loanId, transactionId, amount.toString(), 0, 
responseSpec);
+        reviewLoanTransactionRelations(loanId, transactionId, 1);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf(amountVal));
+
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+        GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule = 
getLoansLoanIdResponse.getRepaymentSchedule();
+        for (GetLoansLoanIdRepaymentPeriod period : 
getLoanRepaymentSchedule.getPeriods()) {
+            if (period.getPeriod() != null && period.getPeriod() == 3) {
+                log.info("Period number {} for due date {} and 
totalDueForPeriod {}", period.getPeriod(), period.getDueDate(),
+                        period.getTotalDueForPeriod());
+                assertEquals(Double.valueOf("666.67"), 
period.getTotalDueForPeriod());
+            }
+        }
+    }
+
+    @Test
+    public void applyLoanTransactionChargebackOverNoRepaymentType() {
+        // Client and Loan account creation
+        final Integer loanId = createAccounts(15, 1);
+
+        GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        Set<GetLoansLoanIdTransactions> loanTransactions = 
getLoansLoanIdResponse.getTransactions();
+        assertNotNull(loanTransactions);
+        log.info("Loan Id {} with {} transactions", loanId, 
loanTransactions.size());
+        assertEquals(2, loanTransactions.size());
+        GetLoansLoanIdTransactions loanTransaction = 
loanTransactions.iterator().next();
+        log.info("Try to apply the Charge back over transaction Id {} with 
type {}", loanTransaction.getId(),
+                loanTransaction.getType().getCode());
+
+        applyChargebackTransaction(loanId, loanTransaction.getId().intValue(), 
amountVal, 0, responseSpecError);
+    }
+
+    @Test
+    public void applyLoanTransactionChargebackAfterMature() {
+        // Client and Loan account creation
+        final Integer loanId = createAccounts(45, 1);
 
         GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
         assertNotNull(getLoansLoanIdResponse);
 
         loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+        GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule = 
getLoansLoanIdResponse.getRepaymentSchedule();
+        log.info("Loan with {} periods", 
getLoanRepaymentSchedule.getPeriods().size());
+        assertEquals(2, getLoanRepaymentSchedule.getPeriods().size());
 
-        operationDate = Utils.dateFormatter.format(todaysDate);
-        Float amount = Float.valueOf("1100.0");
+        Float amount = Float.valueOf(amountVal);
         PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
                 loanId);
         assertNotNull(loanIdTransactionsResponse);
         final Integer transactionId = 
loanIdTransactionsResponse.getResourceId();
 
-        List<GetPaymentTypesResponse> paymentTypeList = 
PaymentTypeHelper.getSystemPaymentType(requestSpec, responseSpec);
-        assertTrue(!paymentTypeList.isEmpty());
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.closed.obligations.met");
+
+        reviewLoanTransactionRelations(loanId, transactionId, 0);
+
+        Integer chargebackTransactionId = applyChargebackTransaction(loanId, 
transactionId, "500.00", 0, responseSpec);
+
+        reviewLoanTransactionRelations(loanId, transactionId, 1);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.active");
+
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf("500.00"));
+
+        // N+1 Scenario
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+        getLoanRepaymentSchedule = 
getLoansLoanIdResponse.getRepaymentSchedule();
+        log.info("Loan with {} periods", 
getLoanRepaymentSchedule.getPeriods().size());
+        assertEquals(3, getLoanRepaymentSchedule.getPeriods().size());
+        getLoanRepaymentSchedule = 
getLoansLoanIdResponse.getRepaymentSchedule();
+        for (GetLoansLoanIdRepaymentPeriod period : 
getLoanRepaymentSchedule.getPeriods()) {
+            if (period.getPeriod() != null && period.getPeriod() == 2) {
+                log.info("Period number {} for due date {} and 
totalDueForPeriod {}", period.getPeriod(), period.getDueDate(),
+                        period.getTotalDueForPeriod());
+                assertEquals(Double.valueOf("500.00"), 
period.getPrincipalDue());
+            }
+        }
+
+        chargebackTransactionId = applyChargebackTransaction(loanId, 
transactionId, "300.00", 0, responseSpec);
+
+        reviewLoanTransactionRelations(loanId, transactionId, 2);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.active");
+
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf("800.00"));
+
+        // N+1 Scenario -- Remains the same periods number
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+        getLoanRepaymentSchedule = 
getLoansLoanIdResponse.getRepaymentSchedule();
+        log.info("Loan with {} periods", 
getLoanRepaymentSchedule.getPeriods().size());
+        assertEquals(3, getLoanRepaymentSchedule.getPeriods().size());
+        getLoanRepaymentSchedule = 
getLoansLoanIdResponse.getRepaymentSchedule();
+        for (GetLoansLoanIdRepaymentPeriod period : 
getLoanRepaymentSchedule.getPeriods()) {
+            if (period.getPeriod() != null && period.getPeriod() == 2) {
+                log.info("Period number {} for due date {} and 
totalDueForPeriod {}", period.getPeriod(), period.getDueDate(),
+                        period.getTotalDueForPeriod());
+                assertEquals(Double.valueOf("800.00"), 
period.getPrincipalDue());
+            }
+        }
+    }
+
+    @Test
+    public void applyLoanTransactionChargebackWithLoanOverpaidToLoanActive() {
+        // Client and Loan account creation
+        final Integer loanId = createAccounts(15, 1);
+
+        GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        Float amount = Float.valueOf("1100.00");
+        PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+                loanId);
+        assertNotNull(loanIdTransactionsResponse);
+        final Integer transactionId = 
loanIdTransactionsResponse.getResourceId();
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.overpaid");
+
+        reviewLoanTransactionRelations(loanId, transactionId, 0);
 
-        amount = Float.valueOf("800.0");
-        final String payload = createChargebackPayload(amount.toString(), 
paymentTypeList.get(0).getId());
-        PostLoansLoanIdTransactionsTransactionIdRequest 
postLoansTransactionCommandRequest = loanTransactionHelper
-                .applyLoanTransactionCommand(loanId, transactionId, 
"chargeback", payload);
-        assertNotNull(postLoansTransactionCommandRequest);
+        final Integer chargebackTransactionId = 
applyChargebackTransaction(loanId, transactionId, "200.00", 0, responseSpec);
+
+        reviewLoanTransactionRelations(loanId, chargebackTransactionId, 1);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.active");
+
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf("100.00"));
+    }
+
+    @Test
+    public void applyLoanTransactionChargebackWithLoanOverpaidToLoanClose() {
+        // Client and Loan account creation
+        final Integer loanId = createAccounts(15, 1);
+
+        GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        Float amount = Float.valueOf("1100.00");
+        PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+                loanId);
+        assertNotNull(loanIdTransactionsResponse);
+        final Integer transactionId = 
loanIdTransactionsResponse.getResourceId();
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.overpaid");
+
+        reviewLoanTransactionRelations(loanId, transactionId, 0);
+
+        final Integer chargebackTransactionId = 
applyChargebackTransaction(loanId, transactionId, "100.00", 0, responseSpec);
+
+        reviewLoanTransactionRelations(loanId, chargebackTransactionId, 1);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.closed.obligations.met");
+
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf("0.00"));
+    }
+
+    @Test
+    public void 
applyLoanTransactionChargebackWithLoanOverpaidToKeepAsLoanOverpaid() {
+        // Client and Loan account creation
+        final Integer loanId = createAccounts(15, 1);
+
+        GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        Float amount = Float.valueOf("1100.00");
+        PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+                loanId);
+        assertNotNull(loanIdTransactionsResponse);
+        final Integer transactionId = 
loanIdTransactionsResponse.getResourceId();
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.overpaid");
+
+        reviewLoanTransactionRelations(loanId, transactionId, 0);
+
+        final Integer chargebackTransactionId = 
applyChargebackTransaction(loanId, transactionId, "50.00", 0, responseSpec);
+
+        reviewLoanTransactionRelations(loanId, chargebackTransactionId, 1);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.overpaid");
+
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf("0.00"));
+    }
+
+    @Test
+    public void applyMultipleLoanTransactionChargeback() {
+        // Client and Loan account creation
+        final Integer loanId = createAccounts(15, 1);
+
+        GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        Float amount = Float.valueOf(amountVal);
+        PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+                loanId);
+        assertNotNull(loanIdTransactionsResponse);
+        final Integer transactionId = 
loanIdTransactionsResponse.getResourceId();
 
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
         assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, 
"loanStatusType.closed.obligations.met");
+
+        // First round, empty array
+        reviewLoanTransactionRelations(loanId, transactionId, 0);
+
+        applyChargebackTransaction(loanId, transactionId, "200.00", 0, 
responseSpec);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf("200.00"));
+
+        // Second round, array size equal to 1
+        reviewLoanTransactionRelations(loanId, transactionId, 1);
+
+        applyChargebackTransaction(loanId, transactionId, "300.00", 1, 
responseSpec);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf("500.00"));
+
+        // Third round, array size equal to 2
+        reviewLoanTransactionRelations(loanId, transactionId, 2);
+
+        applyChargebackTransaction(loanId, transactionId, "500.00", 0, 
responseSpec);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+        
loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse,
 Double.valueOf("1000.00"));
+    }
+
+    private Integer createAccounts(final Integer daysToSubtract, final Integer 
numberOfRepayments) {
+        // Client and Loan account creation
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, "01 January 2012");
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProduct(loanTransactionHelper, null);
+
+        // Older date to have more than one overdue installment
+        final LocalDate transactionDate = 
this.todaysDate.minusDays(daysToSubtract + (30 * (numberOfRepayments - 1)));
+        String operationDate = Utils.dateFormatter.format(transactionDate);
+
+        return createLoanAccount(loanTransactionHelper, clientId.toString(), 
getLoanProductsProductResponse.getId().toString(),
+                operationDate, amountVal, numberOfRepayments.toString());
     }
 
     private String createChargebackPayload(final String transactionAmount, 
final Integer paymentTypeId) {
@@ -121,11 +425,11 @@ public class LoanTransactionChargebackTest {
     }
 
     private Integer createLoanAccount(final LoanTransactionHelper 
loanTransactionHelper, final String clientId, final String loanProductId,
-            final String operationDate, final String principalAmount) {
-        final String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal(principalAmount).withLoanTermFrequency("12")
-                
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
-                .withRepaymentFrequencyTypeAsMonths() //
-                .withInterestRatePerPeriod("2") //
+            final String operationDate, final String principalAmount, final 
String numberOfRepayments) {
+        final String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal(principalAmount)
+                
.withLoanTermFrequency(numberOfRepayments).withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments)
+                
.withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsMonths() //
+                .withInterestRatePerPeriod("0") //
                 .withExpectedDisbursementDate(operationDate) //
                 .withInterestTypeAsDecliningBalance() //
                 .withSubmittedOnDate(operationDate) //
@@ -135,4 +439,29 @@ public class LoanTransactionChargebackTest {
         
loanTransactionHelper.disburseLoanWithNetDisbursalAmount(operationDate, loanId, 
principalAmount);
         return loanId;
     }
+
+    private Integer applyChargebackTransaction(final Integer loanId, final 
Integer transactionId, final String amount,
+            final Integer paymentTypeIdx, ResponseSpecification responseSpec) {
+        List<GetPaymentTypesResponse> paymentTypeList = 
PaymentTypeHelper.getSystemPaymentType(this.requestSpec, this.responseSpec);
+        assertTrue(!paymentTypeList.isEmpty());
+
+        final String payload = createChargebackPayload(amount, 
paymentTypeList.get(paymentTypeIdx).getId());
+        log.info("Loan Chargeback: {}", payload);
+        PostLoansLoanIdTransactionsTransactionIdResponse 
postLoansTransactionCommandResponse = loanTransactionHelper
+                .applyLoanTransactionCommand(loanId, transactionId, 
"chargeback", payload, responseSpec);
+        assertNotNull(postLoansTransactionCommandResponse);
+
+        log.info("Loan Chargeback Id: {}", 
postLoansTransactionCommandResponse.getResourceId());
+        return postLoansTransactionCommandResponse.getResourceId();
+    }
+
+    private void reviewLoanTransactionRelations(final Integer loanId, final 
Integer transactionId, final Integer expectedSize) {
+        GetLoansLoanIdTransactionsTransactionIdResponse 
getLoansTransactionResponse = loanTransactionHelper.getLoanTransaction(loanId,
+                transactionId);
+        assertNotNull(getLoansTransactionResponse);
+        assertNotNull(getLoansTransactionResponse.getTransactionRelations());
+        assertEquals(expectedSize, 
getLoansTransactionResponse.getTransactionRelations().size());
+        log.info("Loan with {} Chargeback Transactions", 
getLoansTransactionResponse.getTransactionRelations().size());
+    }
+
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index aeb79b8d9..58a1ddf32 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -45,9 +45,11 @@ import 
org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdSummary;
+import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
-import 
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
+import 
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdResponse;
 import org.apache.fineract.client.models.PutLoansLoanIdResponse;
 import org.apache.fineract.client.util.JSON;
 import org.apache.fineract.integrationtests.common.CommonConstants;
@@ -95,7 +97,6 @@ public class LoanTransactionHelper {
     public GetLoanProductsProductIdResponse getLoanProduct(final Integer 
loanProductId) {
         final String GET_LOANPRODUCT_URL = 
"/fineract-provider/api/v1/loanproducts/" + loanProductId + "?" + 
Utils.TENANT_IDENTIFIER;
         final String response = Utils.performServerGet(this.requestSpec, 
this.responseSpec, GET_LOANPRODUCT_URL);
-        log.info("Response {}", response);
         return GSON.fromJson(response, GetLoanProductsProductIdResponse.class);
     }
 
@@ -240,12 +241,12 @@ public class LoanTransactionHelper {
         return Utils.performServerGet(requestSpec, responseSpec, 
GET_REPAYMENTS_URL, "loanRepaymentScheduleInstallments");
     }
 
-    public PostLoansLoanIdTransactionsTransactionIdRequest 
applyLoanTransactionCommand(final Integer loanId, final Integer transactionId,
-            final String command, final String payload) {
+    public PostLoansLoanIdTransactionsTransactionIdResponse 
applyLoanTransactionCommand(final Integer loanId, final Integer transactionId,
+            final String command, final String payload, final 
ResponseSpecification responseSpec) {
         final String LOAN_TRANSACTION_URL = "/fineract-provider/api/v1/loans/" 
+ loanId + "/transactions/" + transactionId + "?command="
                 + command + "&" + Utils.TENANT_IDENTIFIER;
         final String response = Utils.performServerPost(requestSpec, 
responseSpec, LOAN_TRANSACTION_URL, payload, null);
-        return GSON.fromJson(response, 
PostLoansLoanIdTransactionsTransactionIdRequest.class);
+        return GSON.fromJson(response, 
PostLoansLoanIdTransactionsTransactionIdResponse.class);
     }
 
     public HashMap approveLoan(final String approvalDate, final Integer 
loanID) {
@@ -347,7 +348,6 @@ public class LoanTransactionHelper {
             url = createLoanOperationURL(UNDO_DISBURSE_LOAN_COMMAND, loanId);
         }
         final String response = Utils.performServerPost(this.requestSpec, 
this.responseSpec, url, undoBodyJson, null);
-        log.info("Response {}", response);
         return GSON.fromJson(response, PostLoansLoanIdResponse.class);
     }
 
@@ -444,11 +444,13 @@ public class LoanTransactionHelper {
     }
 
     public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final String 
date, final Float amountToBePaid, final Integer loanID) {
+        log.info("Repayment with amount {} in {} for Loan {}", amountToBePaid, 
date, loanID);
         return 
postLoanTransaction(createLoanTransactionURL(MAKE_REPAYMENT_COMMAND, loanID), 
getRepaymentBodyAsJSON(date, amountToBePaid));
     }
 
-    public PostLoansLoanIdTransactionsResponse reverseLoanRepayment(final 
Integer loanId, final Integer transactionId, String date) {
-        return postLoanTransaction(createLoanTransactionURL(UNDO, loanId, 
transactionId), getUndoJsonBody(date));
+    public PostLoansLoanIdTransactionsResponse reverseLoanTransaction(final 
Integer loanId, final Integer transactionId, String date,
+            ResponseSpecification responseSpec) {
+        return postLoanTransaction(createLoanTransactionURL(UNDO, loanId, 
transactionId), getUndoJsonBody(date), responseSpec);
     }
 
     public HashMap makeRepaymentWithPDC(final String date, final Float 
amountToBePaid, final Integer loanID, final Integer paymentType) {
@@ -545,6 +547,13 @@ public class LoanTransactionHelper {
         return Utils.performServerGet(requestSpec, responseSpec, 
GET_LOAN_CHARGES_URL, param);
     }
 
+    public GetLoansLoanIdTransactionsTransactionIdResponse 
getLoanTransaction(final Integer loanId, final Integer txnId) {
+        final String GET_LOAN_CHARGES_URL = "/fineract-provider/api/v1/loans/" 
+ loanId + "/transactions/" + txnId + "?"
+                + Utils.TENANT_IDENTIFIER;
+        final String response = Utils.performServerGet(requestSpec, 
responseSpec, GET_LOAN_CHARGES_URL);
+        return GSON.fromJson(response, 
GetLoansLoanIdTransactionsTransactionIdResponse.class);
+    }
+
     public HashMap getPostDatedCheck(final Integer loanId, final Integer 
installmentId) {
         final String GET_POST_DATED_TRANS_URL = 
"/fineract-provider/api/v1/loans/" + loanId + "/postdatedchecks/" + 
installmentId + "?"
                 + Utils.TENANT_IDENTIFIER;
@@ -859,7 +868,12 @@ public class LoanTransactionHelper {
     }
 
     private PostLoansLoanIdTransactionsResponse postLoanTransaction(final 
String postURLForLoanTransaction, final String jsonToBeSent) {
-        final String response = Utils.performServerPost(this.requestSpec, 
this.responseSpec, postURLForLoanTransaction, jsonToBeSent);
+        return postLoanTransaction(postURLForLoanTransaction, jsonToBeSent, 
this.responseSpec);
+    }
+
+    private PostLoansLoanIdTransactionsResponse postLoanTransaction(final 
String postURLForLoanTransaction, final String jsonToBeSent,
+            ResponseSpecification responseSpec) {
+        final String response = Utils.performServerPost(this.requestSpec, 
responseSpec, postURLForLoanTransaction, jsonToBeSent);
         return GSON.fromJson(response, 
PostLoansLoanIdTransactionsResponse.class);
     }
 
@@ -1110,4 +1124,19 @@ public class LoanTransactionHelper {
         }
     }
 
+    public void validateLoanStatus(GetLoansLoanIdResponse 
getLoansLoanIdResponse, final String statusCodeExpected) {
+        final String statusCode = getLoansLoanIdResponse.getStatus().getCode();
+        log.info("Loan with Id {} is with Status {}", 
getLoansLoanIdResponse.getId(), statusCode);
+        assertEquals(statusCodeExpected, statusCode);
+    }
+
+    public void validateLoanPrincipalOustandingBalance(GetLoansLoanIdResponse 
getLoansLoanIdResponse, Double amountExpected) {
+        GetLoansLoanIdSummary getLoansLoanIdSummary = 
getLoansLoanIdResponse.getSummary();
+        if (getLoansLoanIdSummary != null) {
+            log.info("Loan with Principal Outstanding Balance {} expected {}", 
getLoansLoanIdSummary.getPrincipalOutstanding(),
+                    amountExpected);
+            assertEquals(amountExpected, 
getLoansLoanIdSummary.getPrincipalOutstanding());
+        }
+    }
+
 }


Reply via email to